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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
#pragma once
#include <git2.h>
#include "Repository.h"
#include "CredentialManager.h"
#include "GitException.h"
#include <wx/uri.h>
#include <fmt/format.h>
namespace vcs
{
namespace git
{
class Remote final
{
private:
git_remote* _remote;
public:
using Ptr = std::shared_ptr<Remote>;
Remote(git_remote* remote) :
_remote(remote)
{}
~Remote()
{
git_remote_free(_remote);
}
void fetch()
{
if (!_remote)
{
throw GitException("Not a valid remote");
}
auto url = wxURI(git_remote_url(_remote));
git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
// We will be asked for credentials when the server asks libgit
options.callbacks.credentials = AcquireCredentials;
options.callbacks.payload = this;
auto remoteName = git_remote_name(_remote);
rMessage() << "Fetching from remote " << remoteName << std::endl;
auto error = git_remote_fetch(_remote, nullptr, &options, "fetch");
GitException::ThrowOnError(error);
rMessage() << "Fetch complete" << std::endl;
}
void push(const Reference& ref)
{
git_push_options options = GIT_PUSH_OPTIONS_INIT;
auto refName = ref.getName();
char* refNamePtr = refName.data();
const git_strarray refspecs = {
&refNamePtr,
1
};
auto url = wxURI(git_remote_url(_remote));
// We will be asked for credentials when the server asks libgit
options.callbacks.credentials = AcquireCredentials;
options.callbacks.payload = this;
auto remoteName = git_remote_name(_remote);
rMessage() << "Pushing to remote " << remoteName << std::endl;
auto error = git_remote_push(_remote, &refspecs, &options);
GitException::ThrowOnError(error);
rMessage() << "Push complete" << std::endl;
}
static Ptr CreateFromName(Repository& repository, const std::string& name)
{
git_remote* remote;
auto error = git_remote_lookup(&remote, repository._get(), name.c_str());
GitException::ThrowOnError(error);
return std::make_shared<Remote>(remote);
}
private:
// Compatibility hack: In Ubuntu 20 we only have older libgit2 versions,
// where git_credential was still called git_cred, map them
#if LIBGIT2_VER_MAJOR < 1
#define git_credential git_cred
#define git_credential_userpass_plaintext_new git_cred_userpass_plaintext_new
#endif
static int AcquireCredentials(git_cred** out, const char* url, const char* username_from_url, unsigned int allowed_types, void* payload)
{
*out = GetCredentialsForRemote(url);
return *out == nullptr ? GIT_PASSTHROUGH : 0;
}
static git_credential* GetCredentialsForRemote(const std::string& remoteUrl)
{
wxURI uri(remoteUrl);
// Create the git:scheme://server string to query the credential manager
auto credentialResource = fmt::format("git:{0}://{1}", uri.GetScheme().ToStdString(), uri.GetServer().ToStdString());
auto userAndPass = CredentialManager::RetrievePassword(credentialResource);
if (!userAndPass.first.empty() && !userAndPass.second.empty())
{
rMessage() << "Found credentials for resource " << credentialResource << " in the credential store" << std::endl;
git_credential* credentials = nullptr;
auto error = git_credential_userpass_plaintext_new(&credentials, userAndPass.first.c_str(), userAndPass.second.c_str());
GitException::ThrowOnError(error);
// Clear out the password
memset(userAndPass.second.data(), '\0', userAndPass.second.length());
return credentials;
}
return nullptr;
}
};
}
}
|