/
Remote.h
139 lines (100 loc) · 3.74 KB
/
Remote.h
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;
}
};
}
}