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 491 : explicit PhaseTimer(uint64_t &slot)
44 491 : : m_slot(slot)
45 491 : , m_start(std::chrono::steady_clock::now())
46 : {
47 : }
48 :
49 491 : ~PhaseTimer()
50 : {
51 982 : m_slot += static_cast<uint64_t>(
52 491 : std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_start).count());
53 491 : }
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 292 : GitHelper::GitHelper(std::shared_ptr<GitBase> git, std::shared_ptr<Logger_if> log_if, int repo_idx)
87 : : log::User()
88 584 : , m_git(git)
89 582 : , m_repo_idx(repo_idx)
90 : {
91 582 : set_logger_if(log_if);
92 292 : }
93 :
94 292 : GitHelper::~GitHelper()
95 : {
96 382 : if (get_auto_close() && get_git() && get_git()->is_open()) close_on_error(log::Level::DEBUG);
97 584 : }
98 :
99 317 : void GitHelper::debug(int level, const std::string &msg)
100 : {
101 317 : std::ostringstream oss;
102 :
103 317 : init_oss(oss, msg);
104 :
105 317 : clean_cout();
106 317 : log::User::debug(level, oss.str());
107 317 : init_oss_clear();
108 317 : }
109 :
110 9 : void GitHelper::info(const std::string &msg)
111 : {
112 9 : std::ostringstream oss;
113 :
114 9 : init_oss(oss, msg);
115 :
116 9 : clean_cout();
117 9 : log::User::info(oss.str());
118 9 : init_oss_clear();
119 9 : }
120 :
121 23 : void GitHelper::warn(const std::string &msg)
122 : {
123 23 : std::ostringstream oss;
124 :
125 23 : init_oss(oss, msg);
126 :
127 23 : clean_cout();
128 23 : log::User::warn(oss.str());
129 23 : init_oss_clear();
130 23 : }
131 :
132 10 : void GitHelper::error(const std::string &msg)
133 : {
134 10 : std::ostringstream oss;
135 :
136 10 : init_oss(oss, msg);
137 :
138 10 : clean_cout();
139 10 : log::User::error(oss.str());
140 10 : init_oss_clear();
141 10 : }
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 1633 : void GitHelper::log(const std::string &msg, log::Level level, int debug_level)
166 : {
167 1633 : std::ostringstream oss;
168 :
169 1633 : init_oss(oss, msg);
170 :
171 1633 : clean_cout();
172 1633 : log::User::log(oss.str(), level, debug_level);
173 1633 : init_oss_clear();
174 1633 : }
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 165 : void GitHelper::done(const std::string &msg, const std::string &rel_path, uint64_t elapsed_time)
188 : {
189 165 : ETRC_CALL(msg, rel_path, elapsed_time);
190 :
191 165 : std::ostringstream oss;
192 :
193 165 : init_oss(oss, msg);
194 :
195 165 : oss << " done.\n";
196 165 : oss << " path : " << rel_path << "\n";
197 165 : oss << " elapsed time (s): " << (elapsed_time / THOUSAND) << "." << (elapsed_time % THOUSAND);
198 165 : clean_cout();
199 165 : log::User::info(oss.str());
200 165 : }
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 197 : bool GitHelper::phase_timing_env_enabled()
219 : {
220 197 : if (!g_phase_timing_env_cached.has_value()) g_phase_timing_env_cached = read_phase_timing_env();
221 197 : 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 197 : bool GitHelper::phase_timing_enabled()
235 : {
236 197 : if (g_phase_timing_override.has_value()) return *g_phase_timing_override;
237 197 : return phase_timing_env_enabled();
238 : }
239 :
240 152 : const GitPhaseTimings &GitHelper::get_phase_timings() const
241 : {
242 152 : return m_phase_timings;
243 : }
244 :
245 152 : void GitHelper::log_phase_summary()
246 : {
247 152 : 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 278 : void GitHelper::absorb_refresh_ms()
253 : {
254 556 : if (get_git() == nullptr) return;
255 556 : m_phase_timings.refresh_ms += get_git()->take_last_refresh_ms();
256 : }
257 :
258 158 : Result GitHelper::open(const std::string &folder, log::Level log_level, int debug_level)
259 : {
260 158 : Result result;
261 158 : ETRC_CALL_RET(result, folder, log_level, debug_level);
262 :
263 316 : boost::filesystem::path rel_folder = boost::filesystem::relative(folder);
264 :
265 158 : std::string msg = "Opening ...\n path : " + rel_folder.string();
266 :
267 158 : log(msg, log_level, debug_level);
268 :
269 158 : {
270 158 : PhaseTimer timer(m_phase_timings.open_ms);
271 158 : result = get_git()->open(rel_folder.string());
272 158 : }
273 158 : if (result.error()) error("Failed with error ", result.get_result_code_int());
274 474 : return ETRC_RET(ESYSREPO_RESULT(result));
275 158 : }
276 :
277 27 : Result GitHelper::clone(const std::string &url, const std::string &path, bool do_close, log::Level log_level,
278 : int debug_level)
279 : {
280 27 : Result result;
281 27 : 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 27 : result = clone_rev(url, "", path, do_close, log_level, debug_level);
296 81 : return ETRC_RET(ESYSREPO_RESULT(result));
297 27 : }
298 :
299 31 : void GitHelper::cleanup_failed_clone(const std::string &path)
300 : {
301 31 : close_on_error(log::Level::DEBUG);
302 93 : 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 158 : 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 158 : Result result;
318 158 : ETRC_CALL_RET(result, url, rev, path, do_close, log_level, debug_level);
319 :
320 316 : boost::filesystem::path rel_path = boost::filesystem::relative(path);
321 :
322 158 : std::string msg = "Cloning ...\n path : " + rel_path.string();
323 262 : if (options.checkout_rev && !rev.empty()) msg += "\n rev : " + rev;
324 158 : if (options.checkout_rev && options.single_branch) msg += "\n single-branch";
325 316 : msg += "\n url : " + url;
326 :
327 158 : log(msg, log_level, debug_level);
328 :
329 158 : const int attempts = get_attempts() < 1 ? 1 : get_attempts();
330 189 : for (int attempt = 1; attempt <= attempts; ++attempt)
331 : {
332 179 : const uint64_t clone_ms_before = m_phase_timings.clone_ms;
333 179 : {
334 179 : PhaseTimer timer(m_phase_timings.clone_ms);
335 179 : result = get_git()->clone(url, path, rev, options);
336 179 : }
337 179 : absorb_refresh_ms();
338 179 : if (result.ok())
339 : {
340 148 : done("Cloning", rel_path.string(), m_phase_timings.clone_ms - clone_ms_before);
341 148 : break;
342 : }
343 :
344 31 : std::ostringstream oss;
345 31 : oss << "Failed to clone (attempt " << attempt << "/" << attempts << "): " << result;
346 31 : if (attempt < attempts)
347 : {
348 21 : warn(oss.str());
349 21 : cleanup_failed_clone(path);
350 21 : std::this_thread::sleep_for(std::chrono::milliseconds(get_retry_delay_ms()));
351 : }
352 : else
353 : {
354 10 : error(oss.str());
355 10 : cleanup_failed_clone(path);
356 : }
357 31 : }
358 :
359 314 : 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 158 : }
363 :
364 17 : 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 17 : Result result;
368 17 : ETRC_CALL_RET(result, url, temp_path, path, do_close, log_level, debug_level);
369 :
370 17 : auto start_time = std::chrono::steady_clock::now();
371 :
372 34 : boost::filesystem::path rel_temp_path = boost::filesystem::relative(temp_path);
373 :
374 17 : 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 17 : std::string clone_msg;
404 34 : boost::filesystem::path rel_path = boost::filesystem::relative(path);
405 :
406 17 : clone_msg = "Cloning ...\n path : " + rel_path.string() + "\n url : " + url;
407 17 : log(clone_msg, log::Level::INFO);
408 :
409 17 : 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 17 : const int attempts = get_attempts() < 1 ? 1 : get_attempts();
416 17 : for (int attempt = 1; attempt <= attempts; ++attempt)
417 : {
418 17 : {
419 17 : PhaseTimer timer(m_phase_timings.clone_ms);
420 17 : result = get_git()->clone(url, temp_path);
421 17 : }
422 17 : absorb_refresh_ms();
423 17 : 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 17 : if (do_close) close_on_error(log::Level::DEBUG);
442 :
443 17 : 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 17 : int result = boost_no_all::move(temp_path, path, true);
456 17 : 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 17 : else if (result == -2)
463 0 : warn("Failed to delete temp_path : " + temp_path);
464 : }
465 17 : auto stop_time = std::chrono::steady_clock::now();
466 17 : auto d_milli = std::chrono::duration_cast<std::chrono::milliseconds>(stop_time - start_time).count();
467 :
468 17 : done("Cloning", rel_path.string(), static_cast<uint64_t>(d_milli));
469 34 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
470 34 : }
471 :
472 272 : Result GitHelper::close(log::Level log_level, int debug_level)
473 : {
474 272 : Result result;
475 272 : ETRC_CALL_RET(result, log_level, debug_level);
476 :
477 544 : if (!get_git()->is_open()) return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
478 :
479 272 : log("Closing ...", log_level, debug_level);
480 :
481 272 : result = get_git()->close();
482 272 : std::string err_str;
483 272 : 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 544 : log("Closed.", log_level, debug_level);
490 :
491 544 : return ETRC_RET(ESYSREPO_RESULT(result, err_str));
492 272 : }
493 :
494 70 : void GitHelper::close_on_error(log::Level log_level, int debug_level)
495 : {
496 140 : if (!get_git()->is_open())
497 : {
498 31 : log("Called instenal_close on not open repo ", log_level, debug_level);
499 31 : return;
500 : }
501 :
502 39 : log("Closing ...", log_level, debug_level);
503 :
504 39 : auto result = get_git()->close();
505 39 : std::string err_str;
506 39 : if (result.error())
507 : {
508 0 : error("Close git repo failed with error ", result.get_result_code_int());
509 : }
510 : else
511 78 : log("Closed.", log_level, debug_level);
512 39 : }
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 82 : Result GitHelper::fetch(const std::string &remote, log::Level log_level, int debug_level)
534 : {
535 82 : Result result;
536 82 : ETRC_CALL_RET(result, remote, log_level, debug_level);
537 :
538 82 : log("Fetching ...", log_level, debug_level);
539 :
540 82 : const int attempts = get_attempts() < 1 ? 1 : get_attempts();
541 82 : for (int attempt = 1; attempt <= attempts; ++attempt)
542 : {
543 82 : {
544 82 : PhaseTimer timer(m_phase_timings.fetch_ms);
545 82 : result = get_git()->fetch(remote);
546 82 : }
547 82 : absorb_refresh_ms();
548 82 : if (result.ok())
549 : {
550 82 : log("Fetched successfully.", log_level, debug_level);
551 82 : 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 246 : return ETRC_RET(ESYSREPO_RESULT(result));
568 82 : }
569 :
570 81 : Result GitHelper::fetch(log::Level log_level, int debug_level)
571 : {
572 162 : return fetch("", log_level, debug_level);
573 : }
574 :
575 2 : Result GitHelper::fetch_refspec(const std::string &refspec, log::Level log_level, int debug_level)
576 : {
577 2 : Result result;
578 2 : ETRC_CALL_RET(result, refspec, log_level, debug_level);
579 :
580 4 : log("Fetching " + refspec + " ...", log_level, debug_level);
581 2 : result = get_git()->fetch_refspec(refspec);
582 2 : if (result.error())
583 0 : log("Fetch refspec failed.", log::Level::DEBUG);
584 : else
585 4 : log("Fetched " + refspec + ".", log_level, debug_level);
586 6 : return ETRC_RET(ESYSREPO_RESULT(result));
587 2 : }
588 :
589 77 : Result GitHelper::get_branches(git::Branches &branches, git::BranchType branch_type, log::Level log_level,
590 : int debug_level)
591 : {
592 77 : Result result;
593 77 : ETRC_CALL_RET_BEGIN(result, branches, branch_type, log_level, debug_level);
594 77 : ETRC_CALL_RET_OUT_END(branches);
595 :
596 77 : log("Getting branches ...", log::Level::DEBUG);
597 77 : result = get_git()->get_branches(branches, branch_type);
598 77 : if (result.error())
599 0 : error("Failed with error ", result.get_result_code_int());
600 : else
601 154 : log("Got branches.", log::Level::DEBUG);
602 :
603 231 : return ETRC_RET(ESYSREPO_RESULT(result));
604 77 : }
605 :
606 33 : Result_t<bool> GitHelper::has_branch(const std::string &name, git::BranchType branch_type, log::Level log_level,
607 : int debug_level)
608 : {
609 33 : Result_t<bool> result(false);
610 33 : ETRC_CALL_RET(result, name, branch_type, log_level, debug_level);
611 :
612 66 : log("Check branch existance " + name + " ...", log::Level::DEBUG);
613 66 : result = get_git()->has_branch(name, branch_type);
614 33 : if (result)
615 26 : log("Found it.", log::Level::DEBUG);
616 : else
617 40 : log("Not Found", log::Level::DEBUG);
618 66 : return ETRC_RET(ESYSREPO_RESULT_T(result));
619 33 : }
620 :
621 26 : Result GitHelper::get_hash(const std::string &revision, std::string &hash, log::Level log_level, int debug_level)
622 : {
623 26 : Result result;
624 26 : ETRC_CALL_RET_BEGIN(result, revision, hash, log_level, debug_level);
625 26 : ETRC_CALL_RET_OUT_END(hash);
626 :
627 52 : log("Check remote hash for " + revision + " ...", log::Level::DEBUG);
628 26 : result = get_git()->get_hash(revision, hash, git::BranchType::REMOTE);
629 26 : if (result.ok())
630 52 : log("Found it.", log::Level::DEBUG);
631 : else
632 0 : log("Not Found", log::Level::DEBUG);
633 78 : return ETRC_RET(ESYSREPO_RESULT(result));
634 26 : }
635 :
636 0 : Result GitHelper::get_hash_local(const std::string &revision, std::string &hash, log::Level log_level, int debug_level)
637 : {
638 0 : Result result;
639 0 : ETRC_CALL_RET_BEGIN(result, revision, hash, log_level, debug_level);
640 0 : ETRC_CALL_RET_OUT_END(hash);
641 :
642 0 : log("Check local hash for " + revision + " ...", log::Level::DEBUG);
643 0 : result = get_git()->get_hash(revision, hash, git::BranchType::LOCAL);
644 0 : if (result.ok())
645 0 : log("Found it.", log::Level::DEBUG);
646 : else
647 0 : log("Not Found", log::Level::DEBUG);
648 0 : return ETRC_RET(ESYSREPO_RESULT(result));
649 0 : }
650 :
651 38 : Result GitHelper::is_dirty(bool &dirty, log::Level log_level, int debug_level)
652 : {
653 38 : Result result;
654 38 : ETRC_CALL_RET_BEGIN(result, dirty, log_level, debug_level);
655 38 : ETRC_CALL_RET_OUT_END(dirty);
656 :
657 38 : result = get_git()->is_dirty(dirty);
658 38 : if (result.error())
659 0 : error("Failed to check there are changes in the git repo.");
660 : else
661 : {
662 38 : if (dirty)
663 0 : log("Dirty git repo", log_level, debug_level);
664 : else
665 76 : log("No changes in the git repo", log_level, debug_level);
666 : }
667 114 : return ETRC_RET(ESYSREPO_RESULT(result));
668 38 : }
669 :
670 30 : Result GitHelper::is_detached(bool &detached, log::Level log_level, int debug_level)
671 : {
672 30 : Result result;
673 30 : ETRC_CALL_RET_BEGIN(result, detached, log_level, debug_level);
674 30 : ETRC_CALL_RET_OUT_END(detached);
675 :
676 30 : result = get_git()->is_detached(detached);
677 30 : if (result.error())
678 0 : error("Failed to check if the git repo is detached or not.");
679 : else
680 : {
681 30 : if (detached)
682 6 : log("Git repo is detached", log_level, debug_level);
683 : else
684 54 : log("Git repo is not detached", log_level, debug_level);
685 : }
686 90 : return ETRC_RET(ESYSREPO_RESULT(result));
687 30 : }
688 :
689 30 : Result GitHelper::get_status(git::RepoStatus &status, log::Level log_level, int debug_level)
690 : {
691 30 : Result result;
692 30 : ETRC_CALL_RET_BEGIN(result, status, log_level, debug_level);
693 30 : ETRC_CALL_RET_OUT_END(status);
694 :
695 30 : result = get_git()->get_status(status);
696 30 : if (result.error())
697 0 : error("Failed to get the repo status");
698 : else
699 60 : log("Succeeded to get repo status", log_level, debug_level);
700 :
701 90 : return ETRC_RET(ESYSREPO_RESULT(result));
702 30 : }
703 :
704 53 : Result GitHelper::checkout(const std::string &branch, bool force, log::Level log_level, int debug_level)
705 : {
706 53 : Result result;
707 53 : ETRC_CALL_RET(result, branch, force, log_level, debug_level);
708 :
709 53 : {
710 53 : PhaseTimer timer(m_phase_timings.checkout_ms);
711 53 : result = get_git()->checkout(branch, force);
712 53 : }
713 53 : if (result.error())
714 0 : error("Failed to checkout");
715 : else
716 106 : log("Checkout succeeded", log_level, debug_level);
717 159 : return ETRC_RET(ESYSREPO_RESULT(result));
718 53 : }
719 :
720 27 : Result GitHelper::merge_analysis(const std::vector<std::string> &refs, git::MergeAnalysisResult &merge_analysis_result,
721 : std::vector<git::CommitHash> &commits, log::Level log_level, int debug_level)
722 : {
723 27 : Result result;
724 27 : ETRC_CALL_RET_BEGIN(result, refs, merge_analysis_result, commits, log_level, debug_level);
725 27 : ETRC_CALL_RET_OUT_END(merge_analysis_result, commits);
726 :
727 27 : result = get_git()->merge_analysis(refs, merge_analysis_result, commits);
728 27 : if (result.error())
729 0 : error("Merge analysis failed");
730 : else
731 54 : log("Merge analysis succeeded", log_level, debug_level);
732 81 : return ETRC_RET(ESYSREPO_RESULT(result));
733 27 : }
734 :
735 25 : Result GitHelper::move(const std::string &src, const std::string &dst, bool recursive, log::Level log_level,
736 : int debug_level)
737 : {
738 25 : Result rresult;
739 25 : ETRC_CALL_RET(rresult, src, dst, recursive, log_level, debug_level);
740 :
741 50 : boost::filesystem::path abs_src = boost::filesystem::absolute(src);
742 50 : boost::filesystem::path abs_dst = boost::filesystem::absolute(dst);
743 50 : boost::filesystem::path rel_src = boost::filesystem::relative(abs_src);
744 50 : boost::filesystem::path rel_dst = boost::filesystem::relative(abs_dst);
745 25 : std::ostringstream oss;
746 :
747 25 : init_oss(oss);
748 25 : oss << "Move:\n src : " << rel_src.string() << "\n dest : " << rel_dst.string();
749 :
750 : // Use absolute paths: relative paths break if cwd is not the workspace root, and
751 : // follow-copy of Git symlinks must operate on the real tree being installed.
752 25 : int result = log_wrap(boost_no_all::move, log_level)(oss.str(), abs_src.string(), abs_dst.string(), true);
753 25 : if (result == -1) return ETRC_RET(ESYSREPO_RESULT(ResultCode::FAILED_TO_COPY));
754 25 : if (result == -1) return ETRC_RET(ESYSREPO_RESULT(ResultCode::FAILED_TO_REMOVE_ALL));
755 50 : return ETRC_RET(ESYSREPO_RESULT(ResultCode::OK));
756 25 : }
757 :
758 2 : void GitHelper::set_git(std::shared_ptr<GitBase> git)
759 : {
760 2 : m_git = git;
761 2 : }
762 :
763 2101 : std::shared_ptr<GitBase> GitHelper::get_git() const
764 : {
765 2101 : return m_git.lock();
766 : }
767 :
768 32 : void GitHelper::set_auto_close(bool auto_close)
769 : {
770 32 : m_auto_close = auto_close;
771 32 : }
772 :
773 292 : bool GitHelper::get_auto_close() const
774 : {
775 292 : return m_auto_close;
776 : }
777 :
778 0 : void GitHelper::set_repo_idx(int repo_idx)
779 : {
780 0 : m_repo_idx = repo_idx;
781 0 : }
782 :
783 3014 : int GitHelper::get_repo_idx() const
784 : {
785 3014 : return m_repo_idx;
786 : }
787 :
788 13 : void GitHelper::set_display_repo_idx(bool display_repo_idx)
789 : {
790 13 : m_display_repo_idx = display_repo_idx;
791 13 : }
792 :
793 1022 : bool GitHelper::get_display_repo_idx() const
794 : {
795 1022 : return m_display_repo_idx;
796 : }
797 :
798 0 : void GitHelper::set_attempts(int attempts)
799 : {
800 0 : m_attempts = attempts;
801 0 : }
802 :
803 514 : int GitHelper::get_attempts() const
804 : {
805 514 : return m_attempts;
806 : }
807 :
808 0 : void GitHelper::set_retry_delay_ms(int retry_delay_ms)
809 : {
810 0 : m_retry_delay_ms = retry_delay_ms;
811 0 : }
812 :
813 21 : int GitHelper::get_retry_delay_ms() const
814 : {
815 21 : return m_retry_delay_ms;
816 : }
817 :
818 1992 : void GitHelper::init_oss_done()
819 : {
820 1992 : m_init_oss_done = true;
821 1992 : }
822 :
823 1992 : void GitHelper::init_oss_clear()
824 : {
825 1992 : m_init_oss_done = false;
826 1992 : }
827 :
828 2182 : bool GitHelper::is_init_oss_done()
829 : {
830 2182 : return m_init_oss_done;
831 : }
832 :
833 2182 : void GitHelper::init_oss(std::ostringstream &oss)
834 : {
835 2182 : if (is_init_oss_done()) return;
836 :
837 3984 : oss.str("");
838 1992 : if ((get_repo_idx() != -1) && (get_display_repo_idx())) oss << "[" << get_repo_idx() << "] ";
839 1992 : init_oss_done();
840 : }
841 :
842 2157 : void GitHelper::init_oss(std::ostringstream &oss, const std::string &msg)
843 : {
844 2157 : init_oss(oss);
845 :
846 2157 : oss << msg;
847 2157 : }
848 :
849 2157 : void GitHelper::clean_cout()
850 : {
851 2157 : log::ConsoleLockGuard<log::User> lock(this);
852 :
853 2157 : std::string s = " ";
854 2157 : std::cout << "\r";
855 :
856 19413 : for (int idx = 0; idx < CLEAN_OUT_REPETITION; ++idx) std::cout << s;
857 2157 : std::cout << "\r";
858 2157 : }
859 :
860 27 : GitHelper::AutoClose::AutoClose(GitHelper *git_helper)
861 27 : : m_git_helper(git_helper)
862 : {
863 27 : }
864 :
865 27 : GitHelper::AutoClose::~AutoClose()
866 : {
867 27 : if (m_git_helper != nullptr) m_git_helper->close(log::Level::DEBUG);
868 27 : }
869 :
870 : } // namespace esys::repo
|