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 369 : explicit PhaseTimer(uint64_t &slot)
44 369 : : m_slot(slot)
45 369 : , m_start(std::chrono::steady_clock::now())
46 : {
47 : }
48 :
49 369 : ~PhaseTimer()
50 : {
51 738 : m_slot += static_cast<uint64_t>(
52 369 : std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_start).count());
53 369 : }
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 241 : GitHelper::GitHelper(std::shared_ptr<GitBase> git, std::shared_ptr<Logger_if> log_if, int repo_idx)
87 : : log::User()
88 482 : , m_git(git)
89 480 : , m_repo_idx(repo_idx)
90 : {
91 480 : set_logger_if(log_if);
92 241 : }
93 :
94 241 : GitHelper::~GitHelper()
95 : {
96 331 : if (get_auto_close() && get_git() && get_git()->is_open()) close_on_error(log::Level::DEBUG);
97 482 : }
98 :
99 229 : void GitHelper::debug(int level, const std::string &msg)
100 : {
101 229 : std::ostringstream oss;
102 :
103 229 : init_oss(oss, msg);
104 :
105 229 : clean_cout();
106 229 : log::User::debug(level, oss.str());
107 229 : init_oss_clear();
108 229 : }
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 12 : void GitHelper::warn(const std::string &msg)
122 : {
123 12 : std::ostringstream oss;
124 :
125 12 : init_oss(oss, msg);
126 :
127 12 : clean_cout();
128 12 : log::User::warn(oss.str());
129 12 : init_oss_clear();
130 12 : }
131 :
132 5 : void GitHelper::error(const std::string &msg)
133 : {
134 5 : std::ostringstream oss;
135 :
136 5 : init_oss(oss, msg);
137 :
138 5 : clean_cout();
139 5 : log::User::error(oss.str());
140 5 : init_oss_clear();
141 5 : }
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 1284 : void GitHelper::log(const std::string &msg, log::Level level, int debug_level)
166 : {
167 1284 : std::ostringstream oss;
168 :
169 1284 : init_oss(oss, msg);
170 :
171 1284 : clean_cout();
172 1284 : log::User::log(oss.str(), level, debug_level);
173 1284 : init_oss_clear();
174 1284 : }
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 135 : void GitHelper::done(const std::string &msg, const std::string &rel_path, uint64_t elapsed_time)
188 : {
189 135 : ETRC_CALL(msg, rel_path, elapsed_time);
190 :
191 135 : std::ostringstream oss;
192 :
193 135 : init_oss(oss, msg);
194 :
195 135 : oss << " done.\n";
196 135 : oss << " path : " << rel_path << "\n";
197 135 : oss << " elapsed time (s): " << (elapsed_time / THOUSAND) << "." << (elapsed_time % THOUSAND);
198 135 : clean_cout();
199 135 : log::User::info(oss.str());
200 135 : }
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 211 : void GitHelper::absorb_refresh_ms()
253 : {
254 422 : if (get_git() == nullptr) return;
255 422 : 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 23 : Result GitHelper::clone(const std::string &url, const std::string &path, bool do_close, log::Level log_level,
278 : int debug_level)
279 : {
280 23 : Result result;
281 23 : 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 23 : result = clone_rev(url, "", path, do_close, log_level, debug_level);
296 69 : return ETRC_RET(ESYSREPO_RESULT(result));
297 23 : }
298 :
299 15 : void GitHelper::cleanup_failed_clone(const std::string &path)
300 : {
301 15 : close_on_error(log::Level::DEBUG);
302 45 : 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 127 : 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 127 : Result result;
318 127 : ETRC_CALL_RET(result, url, rev, path, do_close, log_level, debug_level);
319 :
320 254 : boost::filesystem::path rel_path = boost::filesystem::relative(path);
321 :
322 127 : std::string msg = "Cloning ...\n path : " + rel_path.string();
323 213 : if (options.checkout_rev && !rev.empty()) msg += "\n rev : " + rev;
324 127 : if (options.checkout_rev && options.single_branch) msg += "\n single-branch";
325 254 : msg += "\n url : " + url;
326 :
327 127 : log(msg, log_level, debug_level);
328 :
329 127 : const int attempts = get_attempts() < 1 ? 1 : get_attempts();
330 142 : for (int attempt = 1; attempt <= attempts; ++attempt)
331 : {
332 137 : const uint64_t clone_ms_before = m_phase_timings.clone_ms;
333 137 : {
334 137 : PhaseTimer timer(m_phase_timings.clone_ms);
335 137 : result = get_git()->clone(url, path, rev, options);
336 137 : }
337 137 : absorb_refresh_ms();
338 137 : if (result.ok())
339 : {
340 122 : done("Cloning", rel_path.string(), m_phase_timings.clone_ms - clone_ms_before);
341 122 : break;
342 : }
343 :
344 15 : std::ostringstream oss;
345 15 : oss << "Failed to clone (attempt " << attempt << "/" << attempts << "): " << result;
346 15 : if (attempt < attempts)
347 : {
348 10 : warn(oss.str());
349 10 : cleanup_failed_clone(path);
350 10 : std::this_thread::sleep_for(std::chrono::milliseconds(get_retry_delay_ms()));
351 : }
352 : else
353 : {
354 5 : error(oss.str());
355 5 : cleanup_failed_clone(path);
356 : }
357 15 : }
358 :
359 252 : 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 127 : }
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 221 : Result GitHelper::close(log::Level log_level, int debug_level)
473 : {
474 221 : Result result;
475 221 : ETRC_CALL_RET(result, log_level, debug_level);
476 :
477 442 : if (!get_git()->is_open()) return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
478 :
479 221 : log("Closing ...", log_level, debug_level);
480 :
481 221 : result = get_git()->close();
482 221 : std::string err_str;
483 221 : 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 442 : log("Closed.", log_level, debug_level);
490 :
491 442 : return ETRC_RET(ESYSREPO_RESULT(result, err_str));
492 221 : }
493 :
494 46 : void GitHelper::close_on_error(log::Level log_level, int debug_level)
495 : {
496 92 : if (!get_git()->is_open())
497 : {
498 15 : log("Called instenal_close on not open repo ", log_level, debug_level);
499 15 : 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 61 : Result GitHelper::fetch(const std::string &remote, log::Level log_level, int debug_level)
534 : {
535 61 : Result result;
536 61 : ETRC_CALL_RET(result, remote, log_level, debug_level);
537 :
538 61 : log("Fetching ...", log_level, debug_level);
539 :
540 61 : const int attempts = get_attempts() < 1 ? 1 : get_attempts();
541 61 : for (int attempt = 1; attempt <= attempts; ++attempt)
542 : {
543 61 : {
544 61 : PhaseTimer timer(m_phase_timings.fetch_ms);
545 61 : result = get_git()->fetch(remote);
546 61 : }
547 61 : absorb_refresh_ms();
548 61 : if (result.ok())
549 : {
550 61 : log("Fetched successfully.", log_level, debug_level);
551 61 : 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 183 : return ETRC_RET(ESYSREPO_RESULT(result));
568 61 : }
569 :
570 60 : Result GitHelper::fetch(log::Level log_level, int debug_level)
571 : {
572 120 : return fetch("", log_level, debug_level);
573 : }
574 :
575 73 : Result GitHelper::get_branches(git::Branches &branches, git::BranchType branch_type, log::Level log_level,
576 : int debug_level)
577 : {
578 73 : Result result;
579 73 : ETRC_CALL_RET_BEGIN(result, branches, branch_type, log_level, debug_level);
580 73 : ETRC_CALL_RET_OUT_END(branches);
581 :
582 73 : log("Getting branches ...", log::Level::DEBUG);
583 73 : result = get_git()->get_branches(branches, branch_type);
584 73 : if (result.error())
585 0 : error("Failed with error ", result.get_result_code_int());
586 : else
587 146 : log("Got branches.", log::Level::DEBUG);
588 :
589 219 : return ETRC_RET(ESYSREPO_RESULT(result));
590 73 : }
591 :
592 9 : Result_t<bool> GitHelper::has_branch(const std::string &name, git::BranchType branch_type, log::Level log_level,
593 : int debug_level)
594 : {
595 9 : Result_t<bool> result(false);
596 9 : ETRC_CALL_RET(result, name, branch_type, log_level, debug_level);
597 :
598 18 : log("Check branch existance " + name + " ...", log::Level::DEBUG);
599 18 : result = get_git()->has_branch(name, branch_type);
600 9 : if (result)
601 4 : log("Found it.", log::Level::DEBUG);
602 : else
603 14 : log("Not Found", log::Level::DEBUG);
604 18 : return ETRC_RET(ESYSREPO_RESULT_T(result));
605 9 : }
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 33 : Result GitHelper::checkout(const std::string &branch, bool force, log::Level log_level, int debug_level)
691 : {
692 33 : Result result;
693 33 : ETRC_CALL_RET(result, branch, force, log_level, debug_level);
694 :
695 33 : {
696 33 : PhaseTimer timer(m_phase_timings.checkout_ms);
697 33 : result = get_git()->checkout(branch, force);
698 33 : }
699 33 : if (result.error())
700 0 : error("Failed to checkout");
701 : else
702 66 : log("Checkout succeeded", log_level, debug_level);
703 99 : return ETRC_RET(ESYSREPO_RESULT(result));
704 33 : }
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 21 : Result GitHelper::move(const std::string &src, const std::string &dst, bool recursive, log::Level log_level,
722 : int debug_level)
723 : {
724 21 : Result rresult;
725 21 : ETRC_CALL_RET(rresult, src, dst, recursive, log_level, debug_level);
726 :
727 42 : boost::filesystem::path abs_src = boost::filesystem::absolute(src);
728 42 : boost::filesystem::path abs_dst = boost::filesystem::absolute(dst);
729 42 : boost::filesystem::path rel_src = boost::filesystem::relative(abs_src);
730 42 : boost::filesystem::path rel_dst = boost::filesystem::relative(abs_dst);
731 21 : std::ostringstream oss;
732 :
733 21 : init_oss(oss);
734 21 : oss << "Move:\n src : " << rel_src.string() << "\n dest : " << rel_dst.string();
735 :
736 : // Use absolute paths: relative paths break if cwd is not the workspace root, and
737 : // follow-copy of Git symlinks must operate on the real tree being installed.
738 21 : int result = log_wrap(boost_no_all::move, log_level)(oss.str(), abs_src.string(), abs_dst.string(), true);
739 21 : if (result == -1) return ETRC_RET(ESYSREPO_RESULT(ResultCode::FAILED_TO_COPY));
740 21 : if (result == -1) return ETRC_RET(ESYSREPO_RESULT(ResultCode::FAILED_TO_REMOVE_ALL));
741 42 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
742 21 : }
743 :
744 2 : void GitHelper::set_git(std::shared_ptr<GitBase> git)
745 : {
746 2 : m_git = git;
747 2 : }
748 :
749 1598 : std::shared_ptr<GitBase> GitHelper::get_git() const
750 : {
751 1598 : return m_git.lock();
752 : }
753 :
754 32 : void GitHelper::set_auto_close(bool auto_close)
755 : {
756 32 : m_auto_close = auto_close;
757 32 : }
758 :
759 241 : bool GitHelper::get_auto_close() const
760 : {
761 241 : return m_auto_close;
762 : }
763 :
764 0 : void GitHelper::set_repo_idx(int repo_idx)
765 : {
766 0 : m_repo_idx = repo_idx;
767 0 : }
768 :
769 2245 : int GitHelper::get_repo_idx() const
770 : {
771 2245 : return m_repo_idx;
772 : }
773 :
774 7 : void GitHelper::set_display_repo_idx(bool display_repo_idx)
775 : {
776 7 : m_display_repo_idx = display_repo_idx;
777 7 : }
778 :
779 708 : bool GitHelper::get_display_repo_idx() const
780 : {
781 708 : return m_display_repo_idx;
782 : }
783 :
784 0 : void GitHelper::set_attempts(int attempts)
785 : {
786 0 : m_attempts = attempts;
787 0 : }
788 :
789 402 : int GitHelper::get_attempts() const
790 : {
791 402 : return m_attempts;
792 : }
793 :
794 0 : void GitHelper::set_retry_delay_ms(int retry_delay_ms)
795 : {
796 0 : m_retry_delay_ms = retry_delay_ms;
797 0 : }
798 :
799 10 : int GitHelper::get_retry_delay_ms() const
800 : {
801 10 : return m_retry_delay_ms;
802 : }
803 :
804 1537 : void GitHelper::init_oss_done()
805 : {
806 1537 : m_init_oss_done = true;
807 1537 : }
808 :
809 1537 : void GitHelper::init_oss_clear()
810 : {
811 1537 : m_init_oss_done = false;
812 1537 : }
813 :
814 1693 : bool GitHelper::is_init_oss_done()
815 : {
816 1693 : return m_init_oss_done;
817 : }
818 :
819 1693 : void GitHelper::init_oss(std::ostringstream &oss)
820 : {
821 1693 : if (is_init_oss_done()) return;
822 :
823 3074 : oss.str("");
824 1537 : if ((get_repo_idx() != -1) && (get_display_repo_idx())) oss << "[" << get_repo_idx() << "] ";
825 1537 : init_oss_done();
826 : }
827 :
828 1672 : void GitHelper::init_oss(std::ostringstream &oss, const std::string &msg)
829 : {
830 1672 : init_oss(oss);
831 :
832 1672 : oss << msg;
833 1672 : }
834 :
835 1672 : void GitHelper::clean_cout()
836 : {
837 1672 : log::ConsoleLockGuard<log::User> lock(this);
838 :
839 1672 : std::string s = " ";
840 1672 : std::cout << "\r";
841 :
842 15048 : for (int idx = 0; idx < CLEAN_OUT_REPETITION; ++idx) std::cout << s;
843 1672 : std::cout << "\r";
844 1672 : }
845 :
846 7 : GitHelper::AutoClose::AutoClose(GitHelper *git_helper)
847 7 : : m_git_helper(git_helper)
848 : {
849 7 : }
850 :
851 7 : GitHelper::AutoClose::~AutoClose()
852 : {
853 7 : if (m_git_helper != nullptr) m_git_helper->close(log::Level::DEBUG);
854 7 : }
855 :
856 : } // namespace esys::repo
|