@@ -366,7 +366,6 @@ repository_fetch(::git_repository* repository, std::string const& remote_name =
366366
367367 status_opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
368368 status_opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
369- status_opts.flags |= GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
370369 status_opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
371370
372371 ::git_status_list* status_list = nullptr ;
@@ -380,26 +379,30 @@ repository_fetch(::git_repository* repository, std::string const& remote_name =
380379 auto const count = ::git_status_list_entrycount (status_list);
381380 for (auto i = 0uz; i != count; ++i) {
382381 auto const * entry = ::git_status_byindex (status_list, i);
383- if (entry->head_to_index == nullptr ) {
384- continue ;
385-
386- } else if (entry->status == GIT_STATUS_WT_NEW or entry->status == GIT_STATUS_IGNORED) {
387- auto path = repository_dir / entry->head_to_index ->new_file .path ;
388- path = std::filesystem::canonical (path);
389-
390- if (not is_subpath (path, repository_dir)) {
391- std::print (
392- stderr,
393- " security: file '{}' is outside of directory of repository at '{}'" ,
394- path.string (),
395- repository_dir.string ());
396- std::terminate ();
397- }
398-
399- std::print (stderr, " info: removing file '{}'" , path.string ());
400- auto ec = std::error_code{};
401- if (not std::filesystem::remove (path, ec)) {
402- std::print (stderr, " error: failed removing file '{}': {}" , path.string (), ec.message ());
382+ assert (entry != nullptr );
383+
384+ if (entry->index_to_workdir != nullptr ) {
385+ if (entry->status == GIT_STATUS_WT_NEW or entry->status == GIT_STATUS_IGNORED) {
386+ auto path = repository_dir / entry->index_to_workdir ->new_file .path ;
387+ path = std::filesystem::canonical (path);
388+
389+ if (not is_subpath (path, repository_dir)) {
390+ std::print (
391+ stderr,
392+ " security: file '{}' is outside of directory of repository at '{}'" ,
393+ path.string (),
394+ repository_dir.string ());
395+ std::terminate ();
396+ }
397+
398+ std::print (stderr, " info: removing file '{}'" , path.string ());
399+
400+ // The untracked files in untracked directories are not listed,
401+ // only the directories themselves.
402+ auto ec = std::error_code{};
403+ if (not std::filesystem::remove_all (path, ec)) {
404+ std::print (stderr, " error: failed removing file '{}': {}" , path.string (), ec.message ());
405+ }
403406 }
404407 }
405408 }
@@ -468,7 +471,7 @@ git_fetch_and_update(std::string const& url, std::string const& rev, std::filesy
468471 ::git_repository_free (repository);
469472 }};
470473
471- if (auto remote_url_o = repository_remote_url (repository, url )) {
474+ if (auto remote_url_o = repository_remote_url (repository)) {
472475 if (*remote_url_o != url) {
473476 std::print (" The repository at {}, does not have a remote with the url {}" , path.string (), url);
474477 return git_error::remote_url_mismatch;
@@ -477,15 +480,20 @@ git_fetch_and_update(std::string const& url, std::string const& rev, std::filesy
477480 return remote_url_o.error ();
478481 }
479482
480- auto fetch = to_bool (flags & git_checkout_flags::force_checkout );
483+ auto fetch = to_bool (flags & git_checkout_flags::force_fetch );
481484 if (auto result = repository_matches_rev (repository, rev)) {
482485 switch (*result) {
483486 case rev_match::rev_not_found:
487+ if (to_bool (flags & git_checkout_flags::fresh_clone)) {
488+ return git_error::rev_not_found;
489+ }
490+ [[fallthrough]];
484491 case rev_match::not_checked_out:
485492 case rev_match::checked_out_branch:
486- fetch = true ;
493+ fetch |= not to_bool (flags & git_checkout_flags::fresh_clone) ;
487494 break ;
488495 case rev_match::checked_out:
496+ // Revisions that are tags or commits will not cause a fetch.
489497 break ;
490498 }
491499
@@ -503,7 +511,7 @@ git_fetch_and_update(std::string const& url, std::string const& rev, std::filesy
503511 if (auto result = repository_matches_rev (repository, rev)) {
504512 switch (*result) {
505513 case rev_match::rev_not_found:
506- return git_error::not_found ;
514+ return git_error::rev_not_found ;
507515 case rev_match::not_checked_out:
508516 checkout = true ;
509517 break ;
@@ -515,8 +523,10 @@ git_fetch_and_update(std::string const& url, std::string const& rev, std::filesy
515523 return result.error ();
516524 }
517525
518- auto clean = to_bool (flags & git_checkout_flags::clean);
519- clean |= checkout;
526+ auto clean = checkout;
527+ clean |= to_bool (flags & git_checkout_flags::force_clean);
528+ // A fresh clone does not need to be cleaned.
529+ clean &= not to_bool (flags & git_checkout_flags::fresh_clone);
520530
521531 if (clean) {
522532 if (auto result = repository_clean (repository); result != git_error::ok) {
@@ -578,28 +588,33 @@ git_fetch_and_update(std::string const& url, std::string const& rev, std::filesy
578588 return git_error::ok;
579589}
580590
581- // [[nodiscard]] git_error git_checkout_or_clone(
582- // std::string const& url, std::string const& branch, std::filesystem::path path, git_checkout_flags flags)
583- // {
584- // if (git_is_checked_out(path)) {
585- // // Check if the repository should be checked out again.
586- //
587- // return git_error::ok;
588- // }
589- //
590- // if (git_is_branch(url, branch)) {
591- // git_clone_shallow(url, branch, path);
592- //
593- // } else if (git_is_tag(url, branch)) {
594- // git_clone(url, path);
595- // git_checkout(path, branch);
596- //
597- // } else {
598- // git_clone(url, path);
599- // git_checkout_oid(path, branch);
600- //
601- // }
602- //
603- // }
591+ [[nodiscard]] git_error git_checkout_or_clone (
592+ std::string const & url, std::string const & rev, std::filesystem::path path, git_checkout_flags flags)
593+ {
594+ // First try and just update the repository.
595+ switch (git_fetch_and_update (url, rev, path, flags)) {
596+ case git_error::ok:
597+ return git_error::ok;
598+
599+ case git_error::rev_not_found:
600+ // Repository was found but not the ref.
601+ return git_error::rev_not_found;
602+
603+ case git_error::not_found:
604+ // The repository was not found, try and clone instead.
605+ break ;
606+
607+ default :
608+ return git_error::error;
609+ }
610+
611+ if (auto r = git_clone (url, rev, path); r != git_error::ok) {
612+ return r;
613+ }
614+
615+ // In case rev is a tag or commit, checkout/update the repository.
616+ flags |= git_checkout_flags::fresh_clone;
617+ return git_fetch_and_update (url, rev, path, flags);
618+ }
604619
605620} // namespace hk
0 commit comments