Line data Source code
1 : /*!
2 : * \file esys/repo/githelper.cpp
3 : * \brief
4 : *
5 : * \cond
6 : * __legal_b__
7 : *
8 : * Copyright (c) 2020-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/githelper.h"
20 : #include "esys/repo/filesystem.h"
21 :
22 : #include <esys/log/consolelockguard.h>
23 :
24 : #include <esys/trace/call.h>
25 : #include <esys/trace/macros.h>
26 :
27 : #include <boost/filesystem.hpp>
28 :
29 : #include <chrono>
30 : #include <cctype>
31 : #include <cstdlib>
32 : #include <optional>
33 : #include <sstream>
34 : #include <thread>
35 : #include <iostream>
36 :
37 : namespace
38 : {
39 :
40 : class PhaseTimer
41 : {
42 : public:
43 392 : explicit PhaseTimer(uint64_t &slot)
44 392 : : m_slot(slot)
45 392 : , m_start(std::chrono::steady_clock::now())
46 : {
47 : }
48 :
49 392 : ~PhaseTimer()
50 : {
51 784 : m_slot += static_cast<uint64_t>(
52 392 : std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_start).count());
53 392 : }
54 :
55 : PhaseTimer(const PhaseTimer &) = delete;
56 : PhaseTimer &operator=(const PhaseTimer &) = delete;
57 :
58 : private:
59 : uint64_t &m_slot;
60 : std::chrono::steady_clock::time_point m_start;
61 : };
62 :
63 : } // namespace
64 :
65 : namespace std
66 : {
67 :
68 : static ostream &operator<<(ostream &os, const vector<string> &strings)
69 : {
70 : os << "{" << std::endl;
71 :
72 : for (auto idx = 0; idx < strings.size(); ++idx)
73 : {
74 : if (idx != 0) os << "," << std::endl;
75 : os << " " << strings[idx];
76 : }
77 : os << std::endl << "}";
78 : return os;
79 : }
80 :
81 : } // namespace std
82 :
83 : namespace esys::repo
84 : {
85 :
86 240 : GitHelper::GitHelper(std::shared_ptr<GitBase> git, std::shared_ptr<Logger_if> log_if, int repo_idx)
87 : : log::User()
88 480 : , m_git(git)
89 478 : , m_repo_idx(repo_idx)
90 : {
91 478 : set_logger_if(log_if);
92 240 : }
93 :
94 240 : GitHelper::~GitHelper()
95 : {
96 330 : if (get_auto_close() && get_git() && get_git()->is_open()) close_on_error(log::Level::DEBUG);
97 480 : }
98 :
99 135 : void GitHelper::debug(int level, const std::string &msg)
100 : {
101 135 : std::ostringstream oss;
102 :
103 135 : init_oss(oss, msg);
104 :
105 135 : clean_cout();
106 135 : log::User::debug(level, oss.str());
107 135 : init_oss_clear();
108 135 : }
109 :
110 7 : void GitHelper::info(const std::string &msg)
111 : {
112 7 : std::ostringstream oss;
113 :
114 7 : init_oss(oss, msg);
115 :
116 7 : clean_cout();
117 7 : log::User::info(oss.str());
118 7 : init_oss_clear();
119 7 : }
120 :
121 2 : void GitHelper::warn(const std::string &msg)
122 : {
123 2 : std::ostringstream oss;
124 :
125 2 : init_oss(oss, msg);
126 :
127 2 : clean_cout();
128 2 : log::User::warn(oss.str());
129 2 : init_oss_clear();
130 2 : }
131 :
132 0 : void GitHelper::error(const std::string &msg)
133 : {
134 0 : std::ostringstream oss;
135 :
136 0 : init_oss(oss, msg);
137 :
138 0 : clean_cout();
139 0 : log::User::error(oss.str());
140 0 : init_oss_clear();
141 0 : }
142 :
143 0 : void GitHelper::critical(const std::string &msg)
144 : {
145 0 : std::ostringstream oss;
146 :
147 0 : init_oss(oss, msg);
148 :
149 0 : clean_cout();
150 0 : log::User::critical(oss.str());
151 0 : init_oss_clear();
152 0 : }
153 :
154 0 : void GitHelper::log(log::Level level, const std::string &msg)
155 : {
156 0 : std::ostringstream oss;
157 :
158 0 : init_oss(oss, msg);
159 :
160 0 : clean_cout();
161 0 : log::User::log(level, oss.str());
162 0 : init_oss_clear();
163 0 : }
164 :
165 1247 : void GitHelper::log(const std::string &msg, log::Level level, int debug_level)
166 : {
167 1247 : std::ostringstream oss;
168 :
169 1247 : init_oss(oss, msg);
170 :
171 1247 : clean_cout();
172 1247 : log::User::log(oss.str(), level, debug_level);
173 1247 : init_oss_clear();
174 1247 : }
175 :
176 0 : void GitHelper::error(const std::string &msg, int result)
177 : {
178 0 : std::ostringstream oss;
179 :
180 0 : init_oss(oss, msg);
181 :
182 0 : clean_cout();
183 0 : log::User::error(oss.str(), result);
184 0 : init_oss_clear();
185 0 : }
186 :
187 134 : void GitHelper::done(const std::string &msg, const std::string &rel_path, uint64_t elapsed_time)
188 : {
189 134 : ETRC_CALL(msg, rel_path, elapsed_time);
190 :
191 134 : std::ostringstream oss;
192 :
193 134 : init_oss(oss, msg);
194 :
195 134 : oss << " done.\n";
196 134 : oss << " path : " << rel_path << "\n";
197 134 : oss << " elapsed time (s): " << (elapsed_time / THOUSAND) << "." << (elapsed_time % THOUSAND);
198 134 : clean_cout();
199 134 : log::User::info(oss.str());
200 134 : }
201 :
202 : namespace
203 : {
204 : std::optional<bool> g_phase_timing_override;
205 : std::optional<bool> g_phase_timing_env_cached;
206 :
207 1 : bool read_phase_timing_env()
208 : {
209 1 : const char *value = std::getenv("ESYSREPO_SYNC_PHASE_TIMING");
210 1 : if (value == nullptr || value[0] == '\0') return false;
211 :
212 0 : std::string s(value);
213 0 : for (char &c : s) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
214 0 : return s == "1" || s == "true" || s == "yes" || s == "on";
215 1 : }
216 : } // namespace
217 :
218 154 : bool GitHelper::phase_timing_env_enabled()
219 : {
220 154 : if (!g_phase_timing_env_cached.has_value()) g_phase_timing_env_cached = read_phase_timing_env();
221 154 : return *g_phase_timing_env_cached;
222 : }
223 :
224 0 : void GitHelper::set_phase_timing(std::optional<bool> enabled)
225 : {
226 0 : g_phase_timing_override = enabled;
227 0 : }
228 :
229 0 : std::optional<bool> GitHelper::get_phase_timing()
230 : {
231 0 : return g_phase_timing_override;
232 : }
233 :
234 154 : bool GitHelper::phase_timing_enabled()
235 : {
236 154 : if (g_phase_timing_override.has_value()) return *g_phase_timing_override;
237 154 : return phase_timing_env_enabled();
238 : }
239 :
240 120 : const GitPhaseTimings &GitHelper::get_phase_timings() const
241 : {
242 120 : return m_phase_timings;
243 : }
244 :
245 120 : void GitHelper::log_phase_summary()
246 : {
247 120 : if (!phase_timing_enabled()) return;
248 0 : if (m_phase_timings.total_ms() == 0 && m_phase_timings.refresh_ms == 0) return;
249 0 : info(m_phase_timings.format());
250 : }
251 :
252 201 : void GitHelper::absorb_refresh_ms()
253 : {
254 402 : if (get_git() == nullptr) return;
255 402 : m_phase_timings.refresh_ms += get_git()->take_last_refresh_ms();
256 : }
257 :
258 123 : Result GitHelper::open(const std::string &folder, log::Level log_level, int debug_level)
259 : {
260 123 : Result result;
261 123 : ETRC_CALL_RET(result, folder, log_level, debug_level);
262 :
263 246 : boost::filesystem::path rel_folder = boost::filesystem::relative(folder);
264 :
265 123 : std::string msg = "Opening ...\n path : " + rel_folder.string();
266 :
267 123 : log(msg, log_level, debug_level);
268 :
269 123 : {
270 123 : PhaseTimer timer(m_phase_timings.open_ms);
271 123 : result = get_git()->open(rel_folder.string());
272 123 : }
273 123 : if (result.error()) error("Failed with error ", result.get_result_code_int());
274 369 : return ETRC_RET(ESYSREPO_RESULT(result));
275 123 : }
276 :
277 22 : Result GitHelper::clone(const std::string &url, const std::string &path, bool do_close, log::Level log_level,
278 : int debug_level)
279 : {
280 22 : Result result;
281 22 : ETRC_CALL_RET(result, url, path, do_close, log_level, debug_level);
282 : /*boost::filesystem::path rel_path = boost::filesystem::relative(path);
283 :
284 : std::string msg = "Cloning ...\n url : " + url + "\n path : " + rel_path.string();
285 :
286 : log(msg, log_level, debug_level);
287 :
288 : int result = get_git()->clone(url, path);
289 : if (result < 0)
290 : error("Failed with error ", result);
291 : else
292 : done("Cloning", get_git()->get_last_cmd_elapsed_time());
293 : if (!do_close) return result;
294 : return close(log::Level::DEBUG);*/
295 22 : result = clone_rev(url, "", path, do_close, log_level, debug_level);
296 66 : return ETRC_RET(ESYSREPO_RESULT(result));
297 22 : }
298 :
299 0 : void GitHelper::cleanup_failed_clone(const std::string &path)
300 : {
301 0 : close_on_error(log::Level::DEBUG);
302 0 : if (path.empty() || !boost::filesystem::exists(path)) return;
303 :
304 0 : boost::system::error_code ec;
305 0 : boost::filesystem::remove_all(path, ec);
306 0 : if (ec)
307 : {
308 0 : std::ostringstream oss;
309 0 : oss << "Failed to remove partial clone path '" << path << "': " << ec.message();
310 0 : warn(oss.str());
311 0 : }
312 : }
313 :
314 121 : Result GitHelper::clone_rev(const std::string &url, const std::string &rev, const std::string &path, bool do_close,
315 : log::Level log_level, int debug_level, const git::CloneOptions &options)
316 : {
317 121 : Result result;
318 121 : ETRC_CALL_RET(result, url, rev, path, do_close, log_level, debug_level);
319 :
320 242 : boost::filesystem::path rel_path = boost::filesystem::relative(path);
321 :
322 121 : std::string msg = "Cloning ...\n path : " + rel_path.string();
323 121 : if (options.checkout_rev && !rev.empty()) msg += "\n rev : " + rev;
324 121 : if (options.checkout_rev && options.single_branch) msg += "\n single-branch";
325 242 : msg += "\n url : " + url;
326 :
327 121 : log(msg, log_level, debug_level);
328 :
329 121 : const int attempts = get_attempts() < 1 ? 1 : get_attempts();
330 121 : for (int attempt = 1; attempt <= attempts; ++attempt)
331 : {
332 121 : const uint64_t clone_ms_before = m_phase_timings.clone_ms;
333 121 : {
334 121 : PhaseTimer timer(m_phase_timings.clone_ms);
335 121 : result = get_git()->clone(url, path, rev, options);
336 121 : }
337 121 : absorb_refresh_ms();
338 121 : if (result.ok())
339 : {
340 121 : done("Cloning", rel_path.string(), m_phase_timings.clone_ms - clone_ms_before);
341 121 : break;
342 : }
343 :
344 0 : std::ostringstream oss;
345 0 : oss << "Failed to clone (attempt " << attempt << "/" << attempts << "): " << result;
346 0 : if (attempt < attempts)
347 : {
348 0 : warn(oss.str());
349 0 : cleanup_failed_clone(path);
350 0 : std::this_thread::sleep_for(std::chrono::milliseconds(get_retry_delay_ms()));
351 : }
352 : else
353 : {
354 0 : error(oss.str());
355 0 : cleanup_failed_clone(path);
356 : }
357 0 : }
358 :
359 240 : if (!do_close) return ETRC_RET(ESYSREPO_RESULT(result));
360 2 : if (result.error()) return ETRC_RET(ESYSREPO_RESULT(result));
361 4 : return ETRC_RET(ESYSREPO_RESULT(close(log::Level::DEBUG)));
362 121 : }
363 :
364 13 : Result GitHelper::clone(const std::string &url, const std::string &temp_path, const std::string &path, bool do_close,
365 : log::Level log_level, int debug_level)
366 : {
367 13 : Result result;
368 13 : ETRC_CALL_RET(result, url, temp_path, path, do_close, log_level, debug_level);
369 :
370 13 : auto start_time = std::chrono::steady_clock::now();
371 :
372 26 : boost::filesystem::path rel_temp_path = boost::filesystem::relative(temp_path);
373 :
374 13 : if (boost::filesystem::exists(temp_path))
375 : {
376 0 : if (log_level == log::Level::DEBUG)
377 : {
378 0 : std::ostringstream oss;
379 :
380 0 : init_oss(oss);
381 0 : oss << "Remove all: " << rel_temp_path.string();
382 :
383 0 : int result = log_wrap(boost_no_all::remove_all, log::Level::DEBUG)(oss.str(), temp_path);
384 0 : if (result < 0)
385 : {
386 0 : std::string err_str = "Failed to remove all : " + rel_temp_path.string();
387 0 : error(err_str);
388 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::RAW_INT_ERROR, result, err_str));
389 0 : }
390 0 : }
391 : else
392 : {
393 0 : int result = boost_no_all::remove_all(temp_path);
394 0 : if (result < 0)
395 : {
396 0 : std::string err_str = "Failed to delete path : " + temp_path;
397 0 : error(err_str);
398 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::RAW_INT_ERROR, result, err_str));
399 0 : }
400 : }
401 : }
402 :
403 13 : std::string clone_msg;
404 26 : boost::filesystem::path rel_path = boost::filesystem::relative(path);
405 :
406 13 : clone_msg = "Cloning ...\n path : " + rel_path.string() + "\n url : " + url;
407 13 : log(clone_msg, log::Level::INFO);
408 :
409 13 : if (log_level == log::Level::DEBUG)
410 : {
411 0 : clone_msg = "Cloning ...\n path : " + rel_temp_path.string() + "\n url : " + url;
412 0 : log(clone_msg, log::Level::DEBUG);
413 : }
414 :
415 13 : const int attempts = get_attempts() < 1 ? 1 : get_attempts();
416 13 : for (int attempt = 1; attempt <= attempts; ++attempt)
417 : {
418 13 : {
419 13 : PhaseTimer timer(m_phase_timings.clone_ms);
420 13 : result = get_git()->clone(url, temp_path);
421 13 : }
422 13 : absorb_refresh_ms();
423 13 : if (result.ok()) break;
424 :
425 0 : std::ostringstream oss;
426 0 : oss << "Failed to clone (attempt " << attempt << "/" << attempts << "): " << result;
427 0 : if (attempt < attempts)
428 : {
429 0 : warn(oss.str());
430 0 : cleanup_failed_clone(temp_path);
431 0 : std::this_thread::sleep_for(std::chrono::milliseconds(get_retry_delay_ms()));
432 : }
433 : else
434 : {
435 0 : error(oss.str());
436 0 : cleanup_failed_clone(temp_path);
437 0 : return ETRC_RET(ESYSREPO_RESULT(result));
438 : }
439 0 : }
440 :
441 13 : if (do_close) close_on_error(log::Level::DEBUG);
442 :
443 13 : if (log_level == log::Level::DEBUG)
444 : {
445 0 : std::ostringstream oss;
446 :
447 0 : init_oss(oss);
448 0 : oss << "Move:\n src : " << rel_temp_path.string() << "\n dest : " << rel_path.string();
449 :
450 0 : int result = log_wrap(boost_no_all::move, log::Level::DEBUG)(oss.str(), temp_path, path, true);
451 0 : if (result < 0) return ETRC_RET(ESYSREPO_RESULT(ResultCode::RAW_INT_ERROR, result));
452 0 : }
453 : else
454 : {
455 13 : int result = boost_no_all::move(temp_path, path, true);
456 13 : if (result == -1)
457 : {
458 0 : std::string err_str = "Failed to copy from temp_path to path : " + temp_path + " -> " + path;
459 0 : error(err_str);
460 0 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::RAW_INT_ERROR, result, err_str));
461 0 : }
462 13 : else if (result == -2)
463 0 : warn("Failed to delete temp_path : " + temp_path);
464 : }
465 13 : auto stop_time = std::chrono::steady_clock::now();
466 13 : auto d_milli = std::chrono::duration_cast<std::chrono::milliseconds>(stop_time - start_time).count();
467 :
468 13 : done("Cloning", rel_path.string(), static_cast<uint64_t>(d_milli));
469 26 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
470 26 : }
471 :
472 172 : Result GitHelper::close(log::Level log_level, int debug_level)
473 : {
474 172 : Result result;
475 172 : ETRC_CALL_RET(result, log_level, debug_level);
476 :
477 344 : if (!get_git()->is_open()) return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
478 :
479 172 : log("Closing ...", log_level, debug_level);
480 :
481 172 : result = get_git()->close();
482 172 : std::string err_str;
483 172 : if (result.error())
484 : {
485 0 : err_str = "Close git repo failed with error ";
486 0 : error("Close git repo failed with error ", result.get_result_code_int());
487 : }
488 : else
489 344 : log("Closed.", log_level, debug_level);
490 :
491 344 : return ETRC_RET(ESYSREPO_RESULT(result, err_str));
492 172 : }
493 :
494 31 : void GitHelper::close_on_error(log::Level log_level, int debug_level)
495 : {
496 62 : if (!get_git()->is_open())
497 : {
498 0 : log("Called instenal_close on not open repo ", log_level, debug_level);
499 0 : return;
500 : }
501 :
502 31 : log("Closing ...", log_level, debug_level);
503 :
504 31 : auto result = get_git()->close();
505 31 : std::string err_str;
506 31 : if (result.error())
507 : {
508 0 : error("Close git repo failed with error ", result.get_result_code_int());
509 : }
510 : else
511 62 : log("Closed.", log_level, debug_level);
512 31 : }
513 :
514 2 : Result GitHelper::fastforward(const git::CommitHash &commit, log::Level log_level, int debug_level)
515 : {
516 2 : Result result;
517 2 : ETRC_CALL_RET(result, commit, log_level, debug_level);
518 :
519 2 : log("Fast forwarding ...", log_level, debug_level);
520 :
521 2 : {
522 2 : PhaseTimer timer(m_phase_timings.fastforward_ms);
523 2 : result = get_git()->fastforward(commit);
524 2 : }
525 2 : if (result.error())
526 0 : error("Failed with error ", result.get_result_code_int());
527 : else
528 4 : log("Fast forwarded successfully.", log_level, debug_level);
529 :
530 6 : return ETRC_RET(ESYSREPO_RESULT(result));
531 2 : }
532 :
533 67 : Result GitHelper::fetch(const std::string &remote, log::Level log_level, int debug_level)
534 : {
535 67 : Result result;
536 67 : ETRC_CALL_RET(result, remote, log_level, debug_level);
537 :
538 67 : log("Fetching ...", log_level, debug_level);
539 :
540 67 : const int attempts = get_attempts() < 1 ? 1 : get_attempts();
541 67 : for (int attempt = 1; attempt <= attempts; ++attempt)
542 : {
543 67 : {
544 67 : PhaseTimer timer(m_phase_timings.fetch_ms);
545 67 : result = get_git()->fetch(remote);
546 67 : }
547 67 : absorb_refresh_ms();
548 67 : if (result.ok())
549 : {
550 67 : log("Fetched successfully.", log_level, debug_level);
551 67 : break;
552 : }
553 :
554 0 : std::ostringstream oss;
555 0 : oss << "Failed to fetch (attempt " << attempt << "/" << attempts << "): " << result;
556 0 : if (attempt < attempts)
557 : {
558 0 : warn(oss.str());
559 0 : std::this_thread::sleep_for(std::chrono::milliseconds(get_retry_delay_ms()));
560 : }
561 : else
562 : {
563 0 : error(oss.str());
564 : }
565 0 : }
566 :
567 201 : return ETRC_RET(ESYSREPO_RESULT(result));
568 67 : }
569 :
570 66 : Result GitHelper::fetch(log::Level log_level, int debug_level)
571 : {
572 132 : return fetch("", log_level, debug_level);
573 : }
574 :
575 86 : Result GitHelper::get_branches(git::Branches &branches, git::BranchType branch_type, log::Level log_level,
576 : int debug_level)
577 : {
578 86 : Result result;
579 86 : ETRC_CALL_RET_BEGIN(result, branches, branch_type, log_level, debug_level);
580 86 : ETRC_CALL_RET_OUT_END(branches);
581 :
582 86 : log("Getting branches ...", log::Level::DEBUG);
583 86 : result = get_git()->get_branches(branches, branch_type);
584 86 : if (result.error())
585 0 : error("Failed with error ", result.get_result_code_int());
586 : else
587 172 : log("Got branches.", log::Level::DEBUG);
588 :
589 258 : return ETRC_RET(ESYSREPO_RESULT(result));
590 86 : }
591 :
592 15 : Result_t<bool> GitHelper::has_branch(const std::string &name, git::BranchType branch_type, log::Level log_level,
593 : int debug_level)
594 : {
595 15 : Result_t<bool> result(false);
596 15 : ETRC_CALL_RET(result, name, branch_type, log_level, debug_level);
597 :
598 30 : log("Check branch existance " + name + " ...", log::Level::DEBUG);
599 30 : result = get_git()->has_branch(name, branch_type);
600 15 : if (result)
601 16 : log("Found it.", log::Level::DEBUG);
602 : else
603 14 : log("Not Found", log::Level::DEBUG);
604 30 : return ETRC_RET(ESYSREPO_RESULT_T(result));
605 15 : }
606 :
607 26 : Result GitHelper::get_hash(const std::string &revision, std::string &hash, log::Level log_level, int debug_level)
608 : {
609 26 : Result result;
610 26 : ETRC_CALL_RET_BEGIN(result, revision, hash, log_level, debug_level);
611 26 : ETRC_CALL_RET_OUT_END(hash);
612 :
613 52 : log("Check remote hash for " + revision + " ...", log::Level::DEBUG);
614 26 : result = get_git()->get_hash(revision, hash, git::BranchType::REMOTE);
615 26 : if (result.ok())
616 52 : log("Found it.", log::Level::DEBUG);
617 : else
618 0 : log("Not Found", log::Level::DEBUG);
619 78 : return ETRC_RET(ESYSREPO_RESULT(result));
620 26 : }
621 :
622 0 : Result GitHelper::get_hash_local(const std::string &revision, std::string &hash, log::Level log_level, int debug_level)
623 : {
624 0 : Result result;
625 0 : ETRC_CALL_RET_BEGIN(result, revision, hash, log_level, debug_level);
626 0 : ETRC_CALL_RET_OUT_END(hash);
627 :
628 0 : log("Check local hash for " + revision + " ...", log::Level::DEBUG);
629 0 : result = get_git()->get_hash(revision, hash, git::BranchType::LOCAL);
630 0 : if (result.ok())
631 0 : log("Found it.", log::Level::DEBUG);
632 : else
633 0 : log("Not Found", log::Level::DEBUG);
634 0 : return ETRC_RET(ESYSREPO_RESULT(result));
635 0 : }
636 :
637 27 : Result GitHelper::is_dirty(bool &dirty, log::Level log_level, int debug_level)
638 : {
639 27 : Result result;
640 27 : ETRC_CALL_RET_BEGIN(result, dirty, log_level, debug_level);
641 27 : ETRC_CALL_RET_OUT_END(dirty);
642 :
643 27 : result = get_git()->is_dirty(dirty);
644 27 : if (result.error())
645 0 : error("Failed to check there are changes in the git repo.");
646 : else
647 : {
648 27 : if (dirty)
649 0 : log("Dirty git repo", log_level, debug_level);
650 : else
651 54 : log("No changes in the git repo", log_level, debug_level);
652 : }
653 81 : return ETRC_RET(ESYSREPO_RESULT(result));
654 27 : }
655 :
656 26 : Result GitHelper::is_detached(bool &detached, log::Level log_level, int debug_level)
657 : {
658 26 : Result result;
659 26 : ETRC_CALL_RET_BEGIN(result, detached, log_level, debug_level);
660 26 : ETRC_CALL_RET_OUT_END(detached);
661 :
662 26 : result = get_git()->is_detached(detached);
663 26 : if (result.error())
664 0 : error("Failed to check if the git repo is detached or not.");
665 : else
666 : {
667 26 : if (detached)
668 6 : log("Git repo is detached", log_level, debug_level);
669 : else
670 46 : log("Git repo is not detached", log_level, debug_level);
671 : }
672 78 : return ETRC_RET(ESYSREPO_RESULT(result));
673 26 : }
674 :
675 30 : Result GitHelper::get_status(git::RepoStatus &status, log::Level log_level, int debug_level)
676 : {
677 30 : Result result;
678 30 : ETRC_CALL_RET_BEGIN(result, status, log_level, debug_level);
679 30 : ETRC_CALL_RET_OUT_END(status);
680 :
681 30 : result = get_git()->get_status(status);
682 30 : if (result.error())
683 0 : error("Failed to get the repo status");
684 : else
685 60 : log("Succeeded to get repo status", log_level, debug_level);
686 :
687 90 : return ETRC_RET(ESYSREPO_RESULT(result));
688 30 : }
689 :
690 66 : Result GitHelper::checkout(const std::string &branch, bool force, log::Level log_level, int debug_level)
691 : {
692 66 : Result result;
693 66 : ETRC_CALL_RET(result, branch, force, log_level, debug_level);
694 :
695 66 : {
696 66 : PhaseTimer timer(m_phase_timings.checkout_ms);
697 66 : result = get_git()->checkout(branch, force);
698 66 : }
699 66 : if (result.error())
700 0 : error("Failed to checkout");
701 : else
702 132 : log("Checkout succeeded", log_level, debug_level);
703 198 : return ETRC_RET(ESYSREPO_RESULT(result));
704 66 : }
705 :
706 23 : Result GitHelper::merge_analysis(const std::vector<std::string> &refs, git::MergeAnalysisResult &merge_analysis_result,
707 : std::vector<git::CommitHash> &commits, log::Level log_level, int debug_level)
708 : {
709 23 : Result result;
710 23 : ETRC_CALL_RET_BEGIN(result, refs, merge_analysis_result, commits, log_level, debug_level);
711 23 : ETRC_CALL_RET_OUT_END(merge_analysis_result, commits);
712 :
713 23 : result = get_git()->merge_analysis(refs, merge_analysis_result, commits);
714 23 : if (result.error())
715 0 : error("Merge analysis failed");
716 : else
717 46 : log("Merge analysis succeeded", log_level, debug_level);
718 69 : return ETRC_RET(ESYSREPO_RESULT(result));
719 23 : }
720 :
721 20 : Result GitHelper::move(const std::string &src, const std::string &dst, bool recursive, log::Level log_level,
722 : int debug_level)
723 : {
724 20 : Result rresult;
725 20 : ETRC_CALL_RET(rresult, src, dst, recursive, log_level, debug_level);
726 :
727 40 : boost::filesystem::path rel_src = boost::filesystem::relative(src);
728 40 : boost::filesystem::path rel_dst = boost::filesystem::relative(dst);
729 20 : std::ostringstream oss;
730 :
731 20 : init_oss(oss);
732 20 : oss << "Move:\n src : " << rel_src.string() << "\n dest : " << rel_dst.string();
733 :
734 20 : int result = log_wrap(boost_no_all::move, log_level)(oss.str(), rel_src.string(), rel_dst.string(), true);
735 20 : if (result == -1) return ETRC_RET(ESYSREPO_RESULT(ResultCode::FAILED_TO_COPY));
736 20 : if (result == -1) return ETRC_RET(ESYSREPO_RESULT(ResultCode::FAILED_TO_REMOVE_ALL));
737 40 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
738 20 : }
739 :
740 2 : void GitHelper::set_git(std::shared_ptr<GitBase> git)
741 : {
742 2 : m_git = git;
743 2 : }
744 :
745 1507 : std::shared_ptr<GitBase> GitHelper::get_git() const
746 : {
747 1507 : return m_git.lock();
748 : }
749 :
750 32 : void GitHelper::set_auto_close(bool auto_close)
751 : {
752 32 : m_auto_close = auto_close;
753 32 : }
754 :
755 240 : bool GitHelper::get_auto_close() const
756 : {
757 240 : return m_auto_close;
758 : }
759 :
760 0 : void GitHelper::set_repo_idx(int repo_idx)
761 : {
762 0 : m_repo_idx = repo_idx;
763 0 : }
764 :
765 1987 : int GitHelper::get_repo_idx() const
766 : {
767 1987 : return m_repo_idx;
768 : }
769 :
770 7 : void GitHelper::set_display_repo_idx(bool display_repo_idx)
771 : {
772 7 : m_display_repo_idx = display_repo_idx;
773 7 : }
774 :
775 596 : bool GitHelper::get_display_repo_idx() const
776 : {
777 596 : return m_display_repo_idx;
778 : }
779 :
780 0 : void GitHelper::set_attempts(int attempts)
781 : {
782 0 : m_attempts = attempts;
783 0 : }
784 :
785 402 : int GitHelper::get_attempts() const
786 : {
787 402 : return m_attempts;
788 : }
789 :
790 0 : void GitHelper::set_retry_delay_ms(int retry_delay_ms)
791 : {
792 0 : m_retry_delay_ms = retry_delay_ms;
793 0 : }
794 :
795 0 : int GitHelper::get_retry_delay_ms() const
796 : {
797 0 : return m_retry_delay_ms;
798 : }
799 :
800 1391 : void GitHelper::init_oss_done()
801 : {
802 1391 : m_init_oss_done = true;
803 1391 : }
804 :
805 1391 : void GitHelper::init_oss_clear()
806 : {
807 1391 : m_init_oss_done = false;
808 1391 : }
809 :
810 1545 : bool GitHelper::is_init_oss_done()
811 : {
812 1545 : return m_init_oss_done;
813 : }
814 :
815 1545 : void GitHelper::init_oss(std::ostringstream &oss)
816 : {
817 1545 : if (is_init_oss_done()) return;
818 :
819 2782 : oss.str("");
820 1391 : if ((get_repo_idx() != -1) && (get_display_repo_idx())) oss << "[" << get_repo_idx() << "] ";
821 1391 : init_oss_done();
822 : }
823 :
824 1525 : void GitHelper::init_oss(std::ostringstream &oss, const std::string &msg)
825 : {
826 1525 : init_oss(oss);
827 :
828 1525 : oss << msg;
829 1525 : }
830 :
831 1525 : void GitHelper::clean_cout()
832 : {
833 1525 : log::ConsoleLockGuard<log::User> lock(this);
834 :
835 1525 : std::string s = " ";
836 1525 : std::cout << "\r";
837 :
838 13725 : for (int idx = 0; idx < CLEAN_OUT_REPETITION; ++idx) std::cout << s;
839 1525 : std::cout << "\r";
840 1525 : }
841 :
842 7 : GitHelper::AutoClose::AutoClose(GitHelper *git_helper)
843 7 : : m_git_helper(git_helper)
844 : {
845 7 : }
846 :
847 7 : GitHelper::AutoClose::~AutoClose()
848 : {
849 7 : if (m_git_helper != nullptr) m_git_helper->close(log::Level::DEBUG);
850 7 : }
851 :
852 : } // namespace esys::repo
|