LCOV - code coverage report
Current view: top level - src/esys/repo/exe - cmdstatus.cpp (source / functions) Hit Total Coverage
Test: esysrepo_coverage.info Lines: 117 188 62.2 %
Date: 2026-11-07 22:18:26 Functions: 12 17 70.6 %

          Line data    Source code
       1             : /*!
       2             :  * \file esys/repo/exe/cmdstatus.cpp
       3             :  * \brief
       4             :  *
       5             :  * \cond
       6             :  * __legal_b__
       7             :  *
       8             :  * Copyright (c) 2020-2021 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/cmdstatus.h"
      20             : #include "esys/repo/exe/nested.h"
      21             : #include "esys/repo/manifest/repository.h"
      22             : #include "esys/repo/githelper.h"
      23             : #include "esys/repo/sharing/occurrence.h"
      24             : 
      25             : #include <termcolor/termcolor.hpp>
      26             : 
      27             : #include <boost/filesystem.hpp>
      28             : 
      29             : #include <nlohmann/json.hpp>
      30             : 
      31             : #include <fstream>
      32             : #include <sstream>
      33             : #include <iomanip>
      34             : 
      35             : namespace esys::repo::exe
      36             : {
      37             : 
      38          18 : CmdStatus::CmdStatus()
      39          36 :     : Cmd("Status")
      40             : {
      41          18 : }
      42             : 
      43          21 : CmdStatus::~CmdStatus() = default;
      44             : 
      45           0 : void CmdStatus::set_quiet(bool quiet)
      46             : {
      47           0 :     m_quiet = quiet;
      48           0 : }
      49             : 
      50           3 : bool CmdStatus::get_quiet() const
      51             : {
      52           3 :     return m_quiet;
      53             : }
      54             : 
      55           3 : Result CmdStatus::impl_run()
      56             : {
      57           3 :     if (get_quiet()) warn("The option --quiet is not implemented yet");
      58             : 
      59           3 :     Result result = process_sub_args_to_find_parent_path();
      60           3 :     if (result.error())
      61             :     {
      62           0 :         std::string err_str = "No ESysRepo folder found";
      63           0 :         error(err_str);
      64           0 :         return ESYSREPO_RESULT(result, ResultCode::CMD_NO_ESYSREPO_FOLDER_FOUND, err_str);
      65           0 :     }
      66             : 
      67           3 :     result = open_esysrepo_folder();
      68           3 :     if (result.error()) return ESYSREPO_RESULT(result);
      69             : 
      70           3 :     result = load_manifest();
      71           3 :     if (result.error()) return ESYSREPO_RESULT(result);
      72             : 
      73           3 :     result = process_sub_args_as_git_repo_paths();
      74           3 :     if (result.error()) return ESYSREPO_RESULT(result);
      75             : 
      76           3 :     std::map<std::string, int> map_input_repo_paths;
      77             : 
      78           3 :     if (get_input_git_repo_paths().size() != 0)
      79             :     {
      80           0 :         for (auto &path : get_input_git_repo_paths())
      81             :         {
      82           0 :             auto repo = get_manifest()->find_repo_by_path(path);
      83           0 :             if (repo != nullptr) map_input_repo_paths[path] = 1;
      84           0 :         }
      85             :     }
      86             : 
      87           3 :     Result rresult;
      88             : 
      89          12 :     for (auto location : get_manifest()->get_locations())
      90             :     {
      91          36 :         for (auto repo : location->get_repos())
      92             :         {
      93          30 :             if (map_input_repo_paths.size() != 0)
      94             :             {
      95           0 :                 if (map_input_repo_paths.find(repo->get_path()) == map_input_repo_paths.end()) continue;
      96             :             }
      97          60 :             result = open_repo(repo);
      98          30 :             if (result.error())
      99             :             {
     100           0 :                 rresult.add(ESYSREPO_RESULT(result));
     101           0 :                 continue;
     102             :             }
     103          60 :             print_repo(repo);
     104          90 :             print_sharing_hints(repo);
     105          30 :         }
     106           6 :     }
     107             : 
     108           3 :     NestedContext nested_scratch;
     109           3 :     NestedContext *nested_ctx = get_nested_context() != nullptr ? get_nested_context() : &nested_scratch;
     110           3 :     set_nested_context(nested_ctx);
     111           3 :     Result nested = for_each_nested(*this, NestedMissingMode::WarnSkip, *nested_ctx,
     112           0 :                                     [this](const std::string &child_abs, std::shared_ptr<manifest::Repository>) {
     113           0 :                                         CmdStatus child;
     114           0 :                                         prepare_nested_cmd(*this, child, child_abs);
     115           0 :                                         child.set_quiet(get_quiet());
     116           0 :                                         return child.run();
     117           3 :                                     });
     118           3 :     if (nested.error()) rresult.add(ESYSREPO_RESULT(nested));
     119             : 
     120           6 :     return ESYSREPO_RESULT(rresult);
     121           6 : }
     122             : 
     123          30 : Result CmdStatus::open_repo(std::shared_ptr<manifest::Repository> repo)
     124             : {
     125          30 :     auto git_helper = new_git_helper();
     126          30 :     git_helper->set_auto_close();
     127             : 
     128          30 :     boost::filesystem::path rel_repo_path;
     129          30 :     boost::filesystem::path repo_path = get_config_folder()->get_workspace_path();
     130          30 :     repo_path /= repo->get_path();
     131          90 :     repo_path = boost::filesystem::absolute(repo_path).lexically_normal().make_preferred();
     132          60 :     rel_repo_path = boost::filesystem::relative(repo_path);
     133             : 
     134          30 :     m_rel_repo_path = rel_repo_path.string();
     135          30 :     m_repo_path = repo_path.string();
     136             : 
     137          30 :     Result result = git_helper->open(repo_path.string(), log::Level::DEBUG);
     138          30 :     if (result.error()) return ESYSREPO_RESULT(result);
     139             : 
     140          30 :     m_repo_status = std::make_shared<git::RepoStatus>();
     141             : 
     142          30 :     result = git_helper->get_status(*m_repo_status, log::Level::DEBUG);
     143          30 :     if (result.error()) return ESYSREPO_RESULT(result);
     144             : 
     145          30 :     m_repo_status->sort_file_status();
     146             : 
     147          30 :     m_branches.clear();
     148             : 
     149          30 :     result = git_helper->get_branches(m_branches, git::BranchType::LOCAL, log::Level::DEBUG);
     150          30 :     if (result.error()) return ESYSREPO_RESULT(result);
     151             : 
     152          30 :     m_branches.sort();
     153             : 
     154          30 :     return ESYSREPO_RESULT(git_helper->close(log::Level::DEBUG));
     155          60 : }
     156             : 
     157          30 : void CmdStatus::print_repo(std::shared_ptr<manifest::Repository> repo)
     158             : {
     159          30 :     std::ostringstream oss;
     160          30 :     std::string first_part;
     161          30 :     std::string cur_branch;
     162             : 
     163          30 :     oss << termcolor::colorize;
     164             : 
     165          30 :     if (m_branches.size() != 0)
     166             :     {
     167          30 :         if (m_branches.get()[0]->get_is_head()) cur_branch = m_branches.get()[0]->get_name();
     168             :     }
     169             : 
     170          30 :     if (m_repo_status->get_file_status().size() == 0)
     171             :     {
     172          81 :         std::string rev = get_manifest()->get_repo_revision(repo);
     173             : 
     174          27 :         auto index = rev.find("refs/heads/");
     175          27 :         if (index != rev.npos) rev = rev.substr(index + std::strlen("refs/heads/"));
     176             : 
     177          27 :         if (rev == cur_branch) return;
     178          27 :     }
     179             : 
     180           3 :     first_part = "Repository : " + repo->get_path();
     181           3 :     oss << std::setw(get_start_print_branch()) << std::setfill(' ') << std::left << first_part;
     182             : 
     183           3 :     if (!cur_branch.empty()) oss << cur_branch;
     184             : 
     185           7 :     for (auto file_status : m_repo_status->get_file_status())
     186             :     {
     187           4 :         oss << std::endl;
     188          12 :         print(oss, file_status);
     189           4 :     }
     190             : 
     191           6 :     info(oss.str());
     192          30 : }
     193             : 
     194          30 : void CmdStatus::print_sharing_hints(std::shared_ptr<manifest::Repository> repo)
     195             : {
     196          60 :     if (repo == nullptr || get_config_folder() == nullptr) return;
     197             : 
     198          30 :     boost::filesystem::path inv_path =
     199          60 :         boost::filesystem::path(get_config_folder()->get_path()) / "sharing-inventory.json";
     200          30 :     if (!boost::filesystem::exists(inv_path)) return;
     201             : 
     202           0 :     std::ifstream ifs(inv_path.string());
     203           0 :     if (!ifs) return;
     204             : 
     205           0 :     nlohmann::json root;
     206           0 :     try
     207             :     {
     208           0 :         ifs >> root;
     209             :     }
     210           0 :     catch (...)
     211             :     {
     212           0 :         return;
     213           0 :     }
     214             : 
     215           0 :     if (!root.contains("occurrences") || !root["occurrences"].is_array()) return;
     216             : 
     217           0 :     const std::string consumer = repo->get_path();
     218           0 :     const int worktree_status = static_cast<int>(sharing::MaterializationStatus::Worktree);
     219             : 
     220           0 :     for (const auto &occ : root["occurrences"])
     221             :     {
     222           0 :         if (!occ.is_object()) continue;
     223           0 :         if (occ.value("consumer", "") != consumer) continue;
     224           0 :         if (occ.value("status", 0) != worktree_status) continue;
     225             : 
     226           0 :         const std::string path = occ.value("path", "");
     227           0 :         const std::string provider = occ.value("provider", "");
     228           0 :         if (path.empty()) continue;
     229             : 
     230           0 :         std::ostringstream oss;
     231           0 :         oss << "  shared worktree: " << path;
     232           0 :         if (!provider.empty()) oss << " <- " << provider;
     233           0 :         info(oss.str());
     234           0 :     }
     235          30 : }
     236             : 
     237           4 : void CmdStatus::print(std::ostream &os, std::shared_ptr<git::FileStatus> file_status)
     238             : {
     239          12 :     os << get_file_status_header(file_status);
     240           4 :     os << "  ";
     241           4 :     if (get_show_file_permission())
     242             :     {
     243           4 :         bool executable = false;
     244           8 :         if (file_status->get_index_to_work_dir() != nullptr)
     245           4 :             executable = (file_status->get_index_to_work_dir()->get_diff_delta().get_old_file().get_mode()
     246             :                           == git::FileMode::BLOB_EXECUTABLE);
     247           4 :         if (executable)
     248           0 :             os << termcolor::red << "x  ";
     249             :         else
     250           4 :             os << "-  ";
     251             :     }
     252             :     else
     253           0 :         os << "   ";
     254             : 
     255           4 :     os << file_status->get_old_name() << termcolor::reset;
     256           4 : }
     257             : 
     258           4 : std::string CmdStatus::get_file_status_header(std::shared_ptr<git::FileStatus> file_status)
     259             : {
     260           4 :     std::string result = "--";
     261           4 :     std::shared_ptr<git::Status> status;
     262             : 
     263           8 :     for (auto status : file_status->get_status())
     264             :     {
     265           4 :         switch (status->get_type())
     266             :         {
     267           0 :             case git::StatusType::CURRENT: result = "--"; break;
     268           0 :             case git::StatusType::INDEX:
     269           0 :                 switch (status->get_sub_type())
     270             :                 {
     271           0 :                     case git::StatusSubType::DELETED: result[0] = 'D'; break;
     272           0 :                     case git::StatusSubType::MODIFIED: result[0] = 'M'; break;
     273           0 :                     case git::StatusSubType::NEW: result[0] = 'A'; break;
     274           0 :                     case git::StatusSubType::RENAMED: result[0] = 'R'; break;
     275           0 :                     case git::StatusSubType::TYPECHANGE: result[0] = 'T'; break;
     276             :                     default:;
     277             :                 }
     278             :                 break;
     279           4 :             case git::StatusType::WORKING_DIR:
     280           4 :                 switch (status->get_sub_type())
     281             :                 {
     282           0 :                     case git::StatusSubType::MODIFIED: result[1] = 'm'; break;
     283           0 :                     case git::StatusSubType::DELETED: result[1] = 'd'; break;
     284             :                     default:;
     285             :                 }
     286             :                 break;
     287           0 :             case git::StatusType::CONFLICTED: result[0] = 'U'; break;
     288           4 :             default:;
     289             :         }
     290           4 :     }
     291             : 
     292           4 :     return result;
     293           4 : }
     294           0 : void CmdStatus::set_start_print_branch(std::size_t start_print_branch)
     295             : {
     296           0 :     m_start_print_branch = start_print_branch;
     297           0 : }
     298             : 
     299           3 : std::size_t CmdStatus::get_start_print_branch() const
     300             : {
     301           3 :     return m_start_print_branch;
     302             : }
     303             : 
     304           0 : void CmdStatus::set_show_file_permission(bool show_file_permission)
     305             : {
     306           0 :     m_show_file_permission = show_file_permission;
     307           0 : }
     308             : 
     309           4 : bool CmdStatus::get_show_file_permission() const
     310             : {
     311           4 :     return m_show_file_permission;
     312             : }
     313             : 
     314           3 : Result CmdStatus::process_sub_args_to_find_parent_path()
     315             : {
     316           3 :     if (!get_workspace_path().empty()) return ESYSREPO_RESULT(ResultCode::OK);
     317             : 
     318           2 :     if ((get_sub_args().size() == 0) || (!get_folder().empty()) || (!get_workspace_path().empty()))
     319           2 :         return ESYSREPO_RESULT(default_handling_folder_workspace());
     320             : 
     321           0 :     std::string parent_path;
     322             : 
     323           0 :     for (auto &path : get_sub_args())
     324             :     {
     325             :         // if (!boost::filesystem::exists(path)) continue;
     326           0 :         parent_path = Cmd::find_workspace_path(path);
     327           0 :         if (!parent_path.empty())
     328             :         {
     329           0 :             set_workspace_path(parent_path);
     330           0 :             return ESYSREPO_RESULT(ResultCode::OK);
     331             :         }
     332             :     }
     333             : 
     334           0 :     parent_path = Cmd::find_workspace_path();
     335           0 :     if (parent_path.empty()) return ESYSREPO_RESULT(ResultCode::CMD_WORKSPACE_PATH_IS_EMPTY);
     336             : 
     337           0 :     set_workspace_path(parent_path);
     338           0 :     return ESYSREPO_RESULT(ResultCode::OK);
     339           3 : }
     340             : 
     341             : } // namespace esys::repo::exe

Generated by: LCOV version 1.14