1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
#include "src/buildtool/file_system/git_tree.hpp"
#include <sstream>
#include "src/buildtool/logging/logger.hpp"
#include "src/utils/cpp/hex_string.hpp"
extern "C" {
#include <git2.h>
}
namespace {
// resolve '.' and '..' in path.
[[nodiscard]] auto ResolveRelativePath(
std::filesystem::path const& path) noexcept -> std::filesystem::path {
auto normalized = path.lexically_normal();
return (normalized / "").parent_path(); // strip trailing slash
}
// NOLINTNEXTLINE(misc-no-recursion)
[[nodiscard]] auto LookupEntryPyPath(
GitTree const& tree,
std::filesystem::path::const_iterator it,
std::filesystem::path::const_iterator const& end) noexcept
-> GitTreeEntryPtr {
auto segment = *it;
auto entry = tree.LookupEntryByName(segment);
if (not entry) {
return nullptr;
}
if (++it != end) {
if (not entry->IsTree()) {
return nullptr;
}
return LookupEntryPyPath(*entry->Tree(), it, end);
}
return entry;
}
} // namespace
auto GitTree::Read(std::filesystem::path const& repo_path,
std::string const& tree_id) noexcept
-> std::optional<GitTree> {
auto cas = GitCAS::Open(repo_path);
if (not cas) {
return std::nullopt;
}
return Read(cas, tree_id);
}
auto GitTree::Read(gsl::not_null<GitCASPtr> const& cas,
std::string const& tree_id) noexcept
-> std::optional<GitTree> {
if (auto raw_id = FromHexString(tree_id)) {
if (auto entries = cas->ReadTree(*raw_id)) {
return GitTree::FromEntries(cas, std::move(*entries), *raw_id);
}
}
return std::nullopt;
}
auto GitTree::LookupEntryByName(std::string const& name) const noexcept
-> GitTreeEntryPtr {
auto entry_it = entries_.find(name);
if (entry_it == entries_.end()) {
Logger::Log(
LogLevel::Error, "git tree does not contain entry {}", name);
return nullptr;
}
return entry_it->second;
}
auto GitTree::LookupEntryByPath(
std::filesystem::path const& path) const noexcept -> GitTreeEntryPtr {
auto resolved = ResolveRelativePath(path);
return LookupEntryPyPath(*this, resolved.begin(), resolved.end());
}
auto GitTree::Size() const noexcept -> std::optional<std::size_t> {
if (auto header = cas_->ReadHeader(raw_id_)) {
return header->first;
}
return std::nullopt;
}
auto GitTreeEntry::Blob() const noexcept -> std::optional<std::string> {
if (not IsBlob()) {
return std::nullopt;
}
return cas_->ReadObject(raw_id_);
}
auto GitTreeEntry::Tree() const& noexcept -> std::optional<GitTree> const& {
return tree_cached_.SetOnceAndGet([this]() -> std::optional<GitTree> {
if (IsTree()) {
if (auto entries = cas_->ReadTree(raw_id_)) {
return GitTree::FromEntries(cas_, std::move(*entries), raw_id_);
}
}
return std::nullopt;
});
}
auto GitTreeEntry::Size() const noexcept -> std::optional<std::size_t> {
if (auto header = cas_->ReadHeader(raw_id_)) {
return header->first;
}
return std::nullopt;
}
|