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;
|
||||
}
|
||||
|
||||
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 reverse, std::string& error) {
|
||||
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) {
|
||||
if (!prepareCredentials(repository, error)) return false;
|
||||
const std::vector<std::string> args = remote.empty()
|
||||
? std::vector<std::string>{"fetch", "--all", "--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) {
|
||||
if (!prepareCredentials(repository, error)) return false;
|
||||
if (mode == 0) return fetch(repository, {}, error);
|
||||
std::vector<std::string> args{"pull"};
|
||||
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) {
|
||||
if (!prepareCredentials(repository, error)) return false;
|
||||
if (runGit(repository, {"push"}, "Push complete", error)) return true;
|
||||
if (repository.remotes.empty()) return false;
|
||||
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,
|
||||
const std::string& url, std::string& error) {
|
||||
repository.credentials_checked = false;
|
||||
return runGit(repository, {"remote", "add", name, url}, "Remote added", error);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ private:
|
||||
void computeGraphLanes(RepositoryView& repository);
|
||||
bool loadRepositoryData(RepositoryView& repository, std::string& error);
|
||||
void loadWorkingTree(RepositoryView& repository);
|
||||
bool prepareCredentials(RepositoryView& repository, std::string& error);
|
||||
bool runGit(RepositoryView& repository, const std::vector<std::string>& arguments,
|
||||
const char* success_message, std::string& message, bool reload = true);
|
||||
};
|
||||
|
||||
@@ -47,6 +47,7 @@ struct RepositoryView {
|
||||
git_repository* repo = nullptr;
|
||||
git_revwalk* commit_walk = nullptr;
|
||||
bool history_exhausted = false;
|
||||
bool credentials_checked = false;
|
||||
std::string path;
|
||||
std::string name = "New Tab";
|
||||
std::string branch = "detached";
|
||||
@@ -70,5 +71,6 @@ struct RepositoryView {
|
||||
commit_walk = nullptr;
|
||||
if (repo) git_repository_free(repo);
|
||||
repo = nullptr;
|
||||
credentials_checked = false;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user