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