feat(git): integrate Git Credential Manager
This commit is contained in:
@@ -434,6 +434,57 @@ bool GitManager::captureGit(RepositoryView& repository, const std::vector<std::s
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GitManager::prepareCredentials(RepositoryView& repository, std::string& error) {
|
||||||
|
if (repository.credentials_checked) return true;
|
||||||
|
|
||||||
|
bool uses_https = false;
|
||||||
|
for (const std::string& remote_name : repository.remotes) {
|
||||||
|
git_remote* remote = nullptr;
|
||||||
|
if (git_remote_lookup(&remote, repository.repo, remote_name.c_str()) == 0) {
|
||||||
|
const char* url = git_remote_url(remote);
|
||||||
|
const std::string remote_url = url ? url : "";
|
||||||
|
uses_https = uses_https || remote_url.starts_with("https://") || remote_url.starts_with("http://");
|
||||||
|
git_remote_free(remote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!uses_https) {
|
||||||
|
repository.credentials_checked = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string helper;
|
||||||
|
std::string helper_error;
|
||||||
|
const bool has_helper = captureGit(repository, {"config", "--get-all", "credential.helper"},
|
||||||
|
helper, helper_error) && !helper.empty();
|
||||||
|
|
||||||
|
std::string gcm_version;
|
||||||
|
std::string gcm_error;
|
||||||
|
const bool gcm_available = captureGit(repository, {"credential-manager", "--version"},
|
||||||
|
gcm_version, gcm_error);
|
||||||
|
|
||||||
|
if (has_helper) {
|
||||||
|
if (helper.find("manager") != std::string::npos && !gcm_available) {
|
||||||
|
error = "Git Credential Manager is configured but unavailable. Reinstall Git for Windows with GCM enabled.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
repository.credentials_checked = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gcm_available) {
|
||||||
|
error = "This HTTPS remote needs authentication, but no credential helper is configured. "
|
||||||
|
"Install Git Credential Manager or configure credential.helper in Git.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string configure_output;
|
||||||
|
if (!captureGit(repository, {"config", "--local", "credential.helper", "manager"},
|
||||||
|
configure_output, error))
|
||||||
|
return false;
|
||||||
|
repository.credentials_checked = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GitManager::applyPatch(RepositoryView& repository, const std::string& patch, bool cached,
|
bool GitManager::applyPatch(RepositoryView& repository, const std::string& patch, bool cached,
|
||||||
bool reverse, std::string& error) {
|
bool reverse, std::string& error) {
|
||||||
const std::filesystem::path patch_path = std::filesystem::temp_directory_path() /
|
const std::filesystem::path patch_path = std::filesystem::temp_directory_path() /
|
||||||
@@ -455,6 +506,7 @@ bool GitManager::applyPatch(RepositoryView& repository, const std::string& patch
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool GitManager::fetch(RepositoryView& repository, const std::string& remote, std::string& error) {
|
bool GitManager::fetch(RepositoryView& repository, const std::string& remote, std::string& error) {
|
||||||
|
if (!prepareCredentials(repository, error)) return false;
|
||||||
const std::vector<std::string> args = remote.empty()
|
const std::vector<std::string> args = remote.empty()
|
||||||
? std::vector<std::string>{"fetch", "--all", "--prune"}
|
? std::vector<std::string>{"fetch", "--all", "--prune"}
|
||||||
: std::vector<std::string>{"fetch", remote, "--prune"};
|
: std::vector<std::string>{"fetch", remote, "--prune"};
|
||||||
@@ -462,6 +514,7 @@ bool GitManager::fetch(RepositoryView& repository, const std::string& remote, st
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool GitManager::pull(RepositoryView& repository, int mode, std::string& error) {
|
bool GitManager::pull(RepositoryView& repository, int mode, std::string& error) {
|
||||||
|
if (!prepareCredentials(repository, error)) return false;
|
||||||
if (mode == 0) return fetch(repository, {}, error);
|
if (mode == 0) return fetch(repository, {}, error);
|
||||||
std::vector<std::string> args{"pull"};
|
std::vector<std::string> args{"pull"};
|
||||||
if (mode == 1) args.push_back("--ff");
|
if (mode == 1) args.push_back("--ff");
|
||||||
@@ -471,6 +524,7 @@ bool GitManager::pull(RepositoryView& repository, int mode, std::string& error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool GitManager::push(RepositoryView& repository, std::string& error) {
|
bool GitManager::push(RepositoryView& repository, std::string& error) {
|
||||||
|
if (!prepareCredentials(repository, error)) return false;
|
||||||
if (runGit(repository, {"push"}, "Push complete", error)) return true;
|
if (runGit(repository, {"push"}, "Push complete", error)) return true;
|
||||||
if (repository.remotes.empty()) return false;
|
if (repository.remotes.empty()) return false;
|
||||||
const std::string remote = std::find(repository.remotes.begin(), repository.remotes.end(), "origin") !=
|
const std::string remote = std::find(repository.remotes.begin(), repository.remotes.end(), "origin") !=
|
||||||
@@ -546,6 +600,7 @@ bool GitManager::createTag(RepositoryView& repository, const std::string& name,
|
|||||||
|
|
||||||
bool GitManager::addRemote(RepositoryView& repository, const std::string& name,
|
bool GitManager::addRemote(RepositoryView& repository, const std::string& name,
|
||||||
const std::string& url, std::string& error) {
|
const std::string& url, std::string& error) {
|
||||||
|
repository.credentials_checked = false;
|
||||||
return runGit(repository, {"remote", "add", name, url}, "Remote added", error);
|
return runGit(repository, {"remote", "add", name, url}, "Remote added", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ private:
|
|||||||
void computeGraphLanes(RepositoryView& repository);
|
void computeGraphLanes(RepositoryView& repository);
|
||||||
bool loadRepositoryData(RepositoryView& repository, std::string& error);
|
bool loadRepositoryData(RepositoryView& repository, std::string& error);
|
||||||
void loadWorkingTree(RepositoryView& repository);
|
void loadWorkingTree(RepositoryView& repository);
|
||||||
|
bool prepareCredentials(RepositoryView& repository, std::string& error);
|
||||||
bool runGit(RepositoryView& repository, const std::vector<std::string>& arguments,
|
bool runGit(RepositoryView& repository, const std::vector<std::string>& arguments,
|
||||||
const char* success_message, std::string& message, bool reload = true);
|
const char* success_message, std::string& message, bool reload = true);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ struct RepositoryView {
|
|||||||
git_repository* repo = nullptr;
|
git_repository* repo = nullptr;
|
||||||
git_revwalk* commit_walk = nullptr;
|
git_revwalk* commit_walk = nullptr;
|
||||||
bool history_exhausted = false;
|
bool history_exhausted = false;
|
||||||
|
bool credentials_checked = false;
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string name = "New Tab";
|
std::string name = "New Tab";
|
||||||
std::string branch = "detached";
|
std::string branch = "detached";
|
||||||
@@ -70,5 +71,6 @@ struct RepositoryView {
|
|||||||
commit_walk = nullptr;
|
commit_walk = nullptr;
|
||||||
if (repo) git_repository_free(repo);
|
if (repo) git_repository_free(repo);
|
||||||
repo = nullptr;
|
repo = nullptr;
|
||||||
|
credentials_checked = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user