Skip to content

Commit

Permalink
#5662: Experimental GitFetch command added. Credential storage and re…
Browse files Browse the repository at this point in the history
…trieval needs to be more sophisticated than that.
  • Loading branch information
codereader committed Jul 4, 2021
1 parent 60c4712 commit 22375f3
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 35 deletions.
1 change: 1 addition & 0 deletions libs/string/encoding.h
Expand Up @@ -3,6 +3,7 @@
#include <string>
#include <codecvt>
#include <locale>
#include <vector>

namespace string
{
Expand Down
42 changes: 42 additions & 0 deletions plugins/vcs/CredentialManager.h
@@ -0,0 +1,42 @@
#pragma once

#include <string>

#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincred.h>
#include <tchar.h>
#include "string/encoding.h"
#endif

namespace vcs
{

class CredentialManager
{
public:
static std::pair<std::string, std::string> RetrievePassword(const std::wstring& accountName)
{
#ifdef _MSC_VER
PCREDENTIALW pcred;
BOOL ok = ::CredReadW(accountName.c_str(), CRED_TYPE_GENERIC, 0, &pcred);

if (!ok)
{
return std::make_pair("", "");
}

auto user = string::unicode_to_utf8(pcred->UserName);
auto pw = string::unicode_to_utf8((const wchar_t*)pcred->CredentialBlob);

::CredFree(pcred);

return std::make_pair(user, pw);
#else
return "";
#endif
}
};

}
42 changes: 38 additions & 4 deletions plugins/vcs/GitModule.cpp
@@ -1,8 +1,10 @@
#include "GitModule.h"

#include "igame.h"
#include "git2.h"
#include "icommandsystem.h"
#include <git2.h>
#include "Repository.h"
#include "Remote.h"

namespace vcs
{
Expand All @@ -23,17 +25,23 @@ void GitModule::initialiseModule(const IApplicationContext& ctx)
{
rMessage() << getName() << "::initialiseModule called." << std::endl;

// Register commands
registerCommands();

// Initialise libgit2 to have the memory handlers etc. set up
git_libgit2_init();

auto modPath = GlobalGameManager().getModPath();
_repository = std::make_unique<git::Repository>(modPath);

git::Repository repository(modPath);

if (repository.isOk())
if (_repository->isOk())
{
rMessage() << "Opened repository at " << modPath << std::endl;
}
else
{
_repository.reset();
}

#if 0
git_commit* commit;
Expand All @@ -54,9 +62,35 @@ void GitModule::shutdownModule()
{
rMessage() << getName() << "::shutdownModule called." << std::endl;

_repository.reset();

git_libgit2_shutdown();
}

void GitModule::registerCommands()
{
GlobalCommandSystem().addCommand("GitFetch", std::bind(&GitModule::fetch, this, std::placeholders::_1));
}

void GitModule::fetch(const cmd::ArgumentList& args)
{
if (!_repository)
{
rWarning() << "Project is not under version control" << std::endl;
return;
}

auto remote = _repository->getRemote("origin");

if (!remote)
{
rWarning() << "Cannot fetch from remote 'origin'" << std::endl;
return;
}

remote->fetch();
}

}

/**
Expand Down
10 changes: 10 additions & 0 deletions plugins/vcs/GitModule.h
@@ -1,19 +1,29 @@
#pragma once

#include "imodule.h"
#include "icommandsystem.h"
#include "Repository.h"

namespace vcs
{

class GitModule :
public RegisterableModule
{
private:
std::unique_ptr<git::Repository> _repository;

public:
// RegisterableModule implementation
const std::string& getName() const override;
const StringSet& getDependencies() const override;
void initialiseModule(const IApplicationContext& ctx) override;
void shutdownModule() override;

private:
void registerCommands();

void fetch(const cmd::ArgumentList& args);
};

}
94 changes: 94 additions & 0 deletions plugins/vcs/Remote.h
@@ -0,0 +1,94 @@
#pragma once

#include <git2.h>
#include "Repository.h"
#include "CredentialManager.h"

namespace vcs
{

namespace git
{

class Remote
{
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)
{
rError() << "Not a valid remote" << std::endl;
return;
}

git_fetch_options options;
git_fetch_options_init(&options, GIT_FETCH_OPTIONS_VERSION);

git_credential* credentials = nullptr;
auto userAndPass = CredentialManager::RetrievePassword(L"git:https://gitlab.com");

if (!userAndPass.first.empty() && !userAndPass.second.empty())
{
if (git_credential_userpass_plaintext_new(&credentials, userAndPass.first.c_str(), userAndPass.second.c_str()) < 0)
{
rError() << "Unable to create credentials" << std::endl;
return;
}

options.callbacks.credentials = AcquireCredentials;
options.callbacks.payload = credentials;
}

auto remoteName = git_remote_name(_remote);

rMessage() << "Fetching from remote " << remoteName << std::endl;

if (git_remote_fetch(_remote, nullptr, &options, "fetch") < 0)
{
const auto* error = git_error_last();

rError() << "Fetch error " << error->message << std::endl;
return;
}

rMessage() << "Fetch complete";
}

static Ptr CreateFromName(Repository& repository, const std::string& name)
{
git_remote* remote;

if (git_remote_lookup(&remote, repository._get(), name.c_str()) < 0)
{
rWarning() << "Failed to look up the remote " << name << std::endl;
return Ptr();
}

return std::make_shared<Remote>(remote);
}

private:
static int AcquireCredentials(git_cred** out, const char* url, const char* username_from_url, unsigned int allowed_types, void* payload)
{
*out = reinterpret_cast<git_credential*>(payload);
return 0;
}
};

}

}
49 changes: 49 additions & 0 deletions plugins/vcs/Repository.cpp
@@ -0,0 +1,49 @@
#include "Repository.h"

#include <git2.h>
#include "itextstream.h"
#include "Remote.h"

namespace vcs
{

namespace git
{

Repository::Repository(const std::string& path) :
_repository(nullptr),
_isOk(false)
{
if (git_repository_open(&_repository, path.c_str()) == 0)
{
_isOk = true;
}
else
{
rMessage() << "Failed to open repository at " << path << std::endl;
}
}

bool Repository::isOk() const
{
return _isOk;
}

Repository::~Repository()
{
git_repository_free(_repository);
}

std::shared_ptr<Remote> Repository::getRemote(const std::string& name)
{
return Remote::CreateFromName(*this, name);
}

git_repository* Repository::_get()
{
return _repository;
}

}

}
46 changes: 18 additions & 28 deletions plugins/vcs/Repository.h
@@ -1,50 +1,40 @@
#pragma once

#include <string>
#include "itextstream.h"
#include "git2.h"
#include <memory>

struct git_repository;

namespace vcs
{

namespace git
{

class Remote;

/**
* Represents a Git repository at a certain path
*/
class Repository
class Repository final
{
private:
git_repository* _repository;
bool _isOk;

public:
Repository(const std::string& path) :
_repository(nullptr),
_isOk(false)
{
if (git_repository_open(&_repository, path.c_str()) == 0)
{
rMessage() << "Opened repository at " << path << std::endl;
_isOk = true;
}
else
{
rMessage() << "Failed to open repository at " << path << std::endl;
}
}

// Status query of this repository object
bool isOk() const
{
return _isOk;
}

~Repository()
{
git_repository_free(_repository);
}
Repository(const std::string& path);
~Repository();

// Status query of this repository object,
// returns true if this repository exists and has been successfully opened
bool isOk() const;

// Returns the remote with the given name
std::shared_ptr<Remote> getRemote(const std::string& name);

// Return the raw libgit2 object
git_repository* _get();
};

}
Expand Down
4 changes: 3 additions & 1 deletion tools/msvc/vcs.vcxproj
Expand Up @@ -168,10 +168,12 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\plugins\vcs\GitModule.cpp" />
<ClCompile Include="..\..\plugins\vcs\plugin.cpp" />
<ClCompile Include="..\..\plugins\vcs\Repository.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\plugins\vcs\CredentialManager.h" />
<ClInclude Include="..\..\plugins\vcs\GitModule.h" />
<ClInclude Include="..\..\plugins\vcs\Remote.h" />
<ClInclude Include="..\..\plugins\vcs\Repository.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
Expand Down

0 comments on commit 22375f3

Please sign in to comment.