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