Skip to content

Commit 8456c6f

Browse files
committed
more git features
1 parent 3d7fedb commit 8456c6f

File tree

2 files changed

+144
-9
lines changed

2 files changed

+144
-9
lines changed

src/utility/git.cpp

Lines changed: 141 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,19 +160,80 @@ class git_lib {
160160
return r;
161161
}
162162

163+
enum class rev_match {
164+
/** Rev was not found in the repository.
165+
*/
166+
rev_not_found,
167+
168+
/** Rev was not checked out.
169+
*/
170+
not_checked_out,
171+
172+
/** Rev was checked out and is a tag or sha.
173+
*/
174+
checked_out,
175+
176+
/** Rev was checked out and is a branch.
177+
*/
178+
checked_out_branch,
179+
};
180+
163181
/** Check if one of the remote has the correct branch checked out.
164182
*
165183
* @param repository The repository to check all the remotes
166-
* @param branch The branch to compare.
167-
* @retval true The correct branch was checked out.
168-
* @retval false Another branch was checked out.
184+
* @param rev The rev to compare.
169185
* @retval error An error during query.
170186
*/
171-
[[nodiscard]] std::expected<bool, git_error> repository_matches_branch(::git_repository *repository, std::string const& branch)
187+
[[nodiscard]] std::expected<rev_match, git_error> repository_matches_rev(::git_repository *repository, std::string const& rev)
172188
{
189+
::git_object *rev_obj = nullptr;
190+
::git_reference *rev_ref = nullptr;
191+
if (auto const result = ::git_revparse_ext(&rev_obj, &rev_ref, repository, rev.c_str()); result != GIT_OK) {
192+
if (result == GIT_ENOTFOUND) {
193+
// The rev was not found in the repository.
194+
return rev_match::rev_not_found;
195+
}
196+
return std::unexpected{make_git_error(result)};
197+
}
198+
auto const d1 = defer{[&] {
199+
::git_object_free(rev_obj);
200+
::git_reference_free(rev_ref);
201+
}};
202+
203+
::git_object *peeled_rev_obj = nullptr;
204+
if (auto const result = ::git_object_peel(&peeled_rev_obj, rev_obj, GIT_OBJECT_COMMIT); result != GIT_OK) {
205+
return std::unexpected{make_git_error(result)};
206+
}
207+
auto const d2 = defer{[&] { ::git_object_free(peeled_rev_obj); }};
208+
209+
::git_reference *head_ref = nullptr;
210+
if (auto const result = ::git_repository_head(&head_ref, repository); result != GIT_OK) {
211+
return std::unexpected{make_git_error(result)};
212+
}
213+
auto const d3 = defer{[&] { ::git_reference_free(head_ref); }};
214+
215+
::git_object *peeled_head_obj = nullptr;
216+
if (auto const result = ::git_reference_peel(&peeled_head_obj, head_ref, GIT_OBJECT_COMMIT); result != GIT_OK) {
217+
return std::unexpected{make_git_error(result)};
218+
}
219+
auto const d4 = defer{[&] { ::git_object_free(peeled_head_obj); }};
220+
221+
auto const *rev_oid = git_object_id(peeled_rev_obj);
222+
assert(rev_oid != nullptr);
173223

224+
auto const *head_oid = git_object_id(peeled_head_obj);
225+
assert(head_oid != nullptr);
226+
227+
if (git_oid_cmp(rev_oid, head_oid) != 0) {
228+
return rev_match::not_checked_out;
229+
} else if (git_reference_is_branch(rev_ref)) {
230+
return rev_match::checked_out_branch;
231+
} else {
232+
return rev_match::checked_out;
233+
}
174234
}
175235

236+
176237
/** Check if one of the remote has a url that matches the given url.
177238
*
178239
* @param repository The repository to check all the remotes
@@ -207,7 +268,30 @@ class git_lib {
207268
return false;
208269
}
209270

210-
[[nodiscard]] git_error git_fetch_and_update(std::string const& url, std::string const& branch, std::filesystem::path path, git_checkout_flags flags)
271+
[[nodiscard]] static git_error repository_fetch(::git_repository *repository, std::string const& remote_name = std::string{"origin"})
272+
{
273+
assert(repository != nullptr);
274+
275+
::git_remote *remote = nullptr;
276+
auto fetch_opts = ::git_fetch_options{};
277+
278+
if (auto result = ::git_fetch_init_options(&fetch_opts, GIT_FETCH_OPTIONS_VERSION); result != 0) {
279+
return git_error::error;
280+
}
281+
282+
if (auto result = ::git_remote_lookup(&remote, repository, remote_name.c_str()); result != GIT_OK) {
283+
return make_git_error(result);
284+
}
285+
defer{[&] { ::git_remote_free(remote); }};
286+
287+
if (auto result = ::git_remote_fetch(remote, nullptr, &fetch_opts, nullptr)) {
288+
return make_git_error(result);
289+
}
290+
291+
return git_error::ok;
292+
}
293+
294+
[[nodiscard]] git_error git_fetch_and_update(std::string const& url, std::string const& rev, std::filesystem::path path, git_checkout_flags flags)
211295
{
212296
auto const& _ = git_lib_initialize();
213297

@@ -228,9 +312,60 @@ class git_lib {
228312
return result.error();
229313
}
230314

231-
if (auto result = repository_matches_branch(repository, branch)) {
315+
auto fetch = to_bool(flags & git_checkout_flags::force_checkout);
316+
if (auto result = repository_matches_rev(repository, rev)) {
317+
switch (*result) {
318+
case rev_match::rev_not_found:
319+
case rev_match::not_checked_out:
320+
case rev_match::checked_out_branch:
321+
fetch = true;
322+
break;
323+
case rev_match::checked_out:
324+
break;
325+
}
326+
327+
} else {
328+
return result.error();
329+
}
330+
331+
if (fetch) {
332+
if (auto result = repository_fetch(repository); result != git_error::ok) {
333+
return result;
334+
}
335+
}
232336

337+
auto checkout = false;
338+
if (auto result = repository_matches_rev(repository, rev)) {
339+
switch (*result) {
340+
case rev_match::rev_not_found:
341+
return git_error::not_found;
342+
case rev_match::not_checked_out:
343+
checkout = true;
344+
break;
345+
case rev_match::checked_out:
346+
case rev_match::checked_out_branch:
347+
break;
348+
}
349+
} else {
350+
return result.error();
233351
}
352+
353+
auto clean = to_bool(flags & git_checkout_flags::clean);
354+
clean |= checkout;
355+
356+
if (clean) {
357+
if (auto result = repository_clean(repository); result != git_error::ok) {
358+
return result;
359+
}
360+
}
361+
362+
if (checkout) {
363+
if (auto result = repository_checkout(repository, rev); result != git_error::ok) {
364+
return result;
365+
}
366+
}
367+
368+
return git_error::ok;
234369
}
235370

236371
[[nodiscard]] git_error git_clone(std::string const& url, std::string const& branch, std::filesystem::path path)

src/utility/git.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,12 @@ class git_references : public std::vector<git_reference> {
139139
*
140140
* @param url The remote url, used to check if the repository at the path
141141
* has the same remote url.
142-
* @param branch The branch to checkout. If the repository is of a different
143-
* branch this branch is checked out, and the repository is cleaned.
142+
* @param rev The rev (branch/tag/sha) to checkout. If the repository is of a different
143+
* rev this branch is checked out, and the repository is cleaned.
144144
* @param path The path where the repository is located.
145145
* @param flags Flags for the way the repository should be checked out.
146146
*/
147-
[[nodiscard]] git_error git_fetch_and_update(std::string const& url, std::string const& branch, std::filesystem::path path, git_checkout_flags flags);
147+
[[nodiscard]] git_error git_fetch_and_update(std::string const& url, std::string const& rev, std::filesystem::path path, git_checkout_flags flags);
148148

149149
/** Checkout or clone the repository.
150150
*

0 commit comments

Comments
 (0)