Skip to content

Commit

Permalink
#5662: Add push algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
codereader committed Jul 16, 2021
1 parent 4121212 commit f51e893
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 41 deletions.
24 changes: 21 additions & 3 deletions plugins/vcs/Algorithm.h
Expand Up @@ -2,6 +2,7 @@

#include "imap.h"
#include "i18n.h"
#include "itextstream.h"
#include "Repository.h"
#include "GitException.h"
#include "Diff.h"
Expand Down Expand Up @@ -106,18 +107,35 @@ inline RemoteStatus analyseRemoteStatus(const std::shared_ptr<Repository>& repos
RequiredMergeStrategy::MergeMap };
}

inline void mergeRemoteChanges(const std::shared_ptr<Repository>& repository)
inline void syncWithRemote(const std::shared_ptr<Repository>& repository)
{
if (GlobalMapModule().isModified())
{
throw git::GitException(_("The map file has unsaved changes, please save before merging."));
}

auto mapPath = repository->getRepositoryRelativePath(GlobalMapModule().getMapName());
auto mapFileHasUncommittedChanges = !mapPath.empty() && repository->fileHasUncommittedChanges(mapPath);
/*auto mapFileHasUncommittedChanges = !mapPath.empty() && repository->fileHasUncommittedChanges(mapPath);
if (mapFileHasUncommittedChanges)
{
throw git::GitException(_("The map file has uncommitted changes, cannot merge yet."));
}
}*/

RemoteStatus status = analyseRemoteStatus(repository);

if (status.strategy == RequiredMergeStrategy::NoMergeRequired)
{
rMessage() << "No merge required." << std::endl;
return;
}

if (status.strategy == RequiredMergeStrategy::JustPush)
{
repository->pushToTrackedRemote();
return;
}

if (!repository->isReadyForMerge())
{
throw git::GitException(_("Repository is not ready for a merge at this point"));
Expand Down
10 changes: 9 additions & 1 deletion plugins/vcs/GitModule.cpp
Expand Up @@ -11,6 +11,7 @@
#include "Repository.h"
#include "Remote.h"
#include "ui/VcsStatus.h"
#include "command/ExecutionFailure.h"

namespace vcs
{
Expand Down Expand Up @@ -85,7 +86,14 @@ void GitModule::fetch(const cmd::ArgumentList& args)
return;
}

_repository->fetchFromTrackedRemote();
try
{
_repository->fetchFromTrackedRemote();
}
catch (const git::GitException& ex)
{
throw cmd::ExecutionFailure(ex.what());
}
}

void GitModule::createPreferencePage()
Expand Down
86 changes: 58 additions & 28 deletions plugins/vcs/Remote.h
Expand Up @@ -3,6 +3,7 @@
#include <git2.h>
#include "Repository.h"
#include "CredentialManager.h"
#include "GitException.h"
#include <wx/uri.h>
#include <fmt/format.h>

Expand Down Expand Up @@ -33,32 +34,18 @@ class Remote final
{
if (!_remote)
{
rError() << "Not a valid remote" << std::endl;
return;
throw GitException("Not a valid remote");
}

auto url = wxURI(git_remote_url(_remote));

git_fetch_options options;
git_fetch_options_init(&options, GIT_FETCH_OPTIONS_VERSION);

// Create the git:scheme://server string to query the credential manager
auto credentialResource = fmt::format("git:{0}://{1}", url.GetScheme().ToStdString(), url.GetServer().ToStdString());

auto userAndPass = CredentialManager::RetrievePassword(credentialResource);
auto credentials = getCredentialsForRemote(url);

if (!userAndPass.first.empty() && !userAndPass.second.empty())
if (credentials != nullptr)
{
rMessage() << "Found credentials for resource " << credentialResource << " in the credential store" << std::endl;

git_credential* credentials = nullptr;

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;
}
Expand All @@ -67,26 +54,47 @@ class Remote final

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

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

rError() << "Fetch error " << error->message << std::endl;
return;
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));
auto credentials = getCredentialsForRemote(url);

if (credentials != nullptr)
{
options.callbacks.credentials = AcquireCredentials;
options.callbacks.payload = credentials;
}

rMessage() << "Fetch complete" << std::endl;
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());

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

return std::make_shared<Remote>(remote);
}
Expand All @@ -97,6 +105,28 @@ class Remote final
*out = reinterpret_cast<git_credential*>(payload);
return 0;
}

git_credential* getCredentialsForRemote(const wxURI& url)
{
// Create the git:scheme://server string to query the credential manager
auto credentialResource = fmt::format("git:{0}://{1}", url.GetScheme().ToStdString(), url.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);

return credentials;
}

return nullptr;
}
};

}
Expand Down
23 changes: 16 additions & 7 deletions plugins/vcs/Repository.cpp
Expand Up @@ -107,14 +107,13 @@ std::string Repository::getUpstreamRemoteName(const Reference& reference)
return upstreamRemote;
}

void Repository::fetchFromTrackedRemote()
Remote::Ptr Repository::getTrackedRemote()
{
auto head = getHead();

if (!head)
{
rWarning() << "Could not retrieve HEAD reference from repository" << std::endl;
return;
throw GitException("Could not retrieve HEAD reference from repository");
}

auto trackedBranch = head->getUpstream();
Expand All @@ -123,8 +122,7 @@ void Repository::fetchFromTrackedRemote()

if (!trackedBranch)
{
rWarning() << "No tracked remote branch configured, cannot fetch" << std::endl;
return;
throw GitException("No tracked remote branch configured");
}

auto remoteName = getUpstreamRemoteName(*head);
Expand All @@ -134,13 +132,24 @@ void Repository::fetchFromTrackedRemote()

if (!remote)
{
rWarning() << "Cannot fetch from remote 'origin'" << std::endl;
return;
throw GitException("Failed to get the named remote: " + remoteName);
}

return remote;
}

void Repository::fetchFromTrackedRemote()
{
auto remote = getTrackedRemote();
remote->fetch();
}

void Repository::pushToTrackedRemote()
{
auto remote = getTrackedRemote();
remote->push(*getHead()); // getHead will succeed because getTrackedRemote did
}

RefSyncStatus Repository::getSyncStatusOfBranch(const Reference& reference)
{
RefSyncStatus status;
Expand Down
5 changes: 5 additions & 0 deletions plugins/vcs/Repository.h
Expand Up @@ -51,6 +51,9 @@ class Repository final
// Performs a fetch from the remote the current branch is tracking
void fetchFromTrackedRemote();

// Pushes the current head to its tracked remote branch
void pushToTrackedRemote();

bool isUpToDateWithRemote();
bool fileIsIndexed(const std::string& relativePath);
bool fileHasUncommittedChanges(const std::string& relativePath);
Expand All @@ -74,6 +77,8 @@ class Repository final
git_repository* _get();

private:
std::shared_ptr<Remote> getTrackedRemote();

unsigned int getFileStatus(const std::string& relativePath);
};

Expand Down
17 changes: 15 additions & 2 deletions plugins/vcs/ui/VcsStatus.cpp
Expand Up @@ -91,8 +91,8 @@ void VcsStatus::createPopupMenu()
));

_popupMenu->addItem(std::make_shared<wxutil::MenuItem>(
new wxMenuItem(nullptr, wxID_ANY, _("Integrate Changes from Server"), ""),
[this]() { if (_repository) { mergeRemoteChanges(_repository); } },
new wxMenuItem(nullptr, wxID_ANY, _("Sync Changes with Server"), ""),
[this]() { performSync(_repository); },
[this]() { return !_fetchInProgress; }
));
}
Expand Down Expand Up @@ -228,6 +228,19 @@ void VcsStatus::performFetch(std::shared_ptr<git::Repository> repository)
_fetchInProgress = false;
}

void VcsStatus::performSync(std::shared_ptr<git::Repository> repository)
{
try
{
repository->pushToTrackedRemote();
setRemoteStatus(git::analyseRemoteStatus(repository));
}
catch (git::GitException& ex)
{
setRemoteStatus(git::RemoteStatus{ 0, 0, ex.what() });
}
}

void VcsStatus::setMapFileStatus(const std::string& status)
{
GlobalUserInterface().dispatch([this, status]() { _mapStatus->SetLabel(status); });
Expand Down
1 change: 1 addition & 0 deletions plugins/vcs/ui/VcsStatus.h
Expand Up @@ -57,6 +57,7 @@ class VcsStatus final :
void onIntervalReached(wxTimerEvent& ev);
void onIdle(wxIdleEvent& ev);
void performFetch(std::shared_ptr<git::Repository> repository);
void performSync(std::shared_ptr<git::Repository> repository);
void performMapFileStatusCheck(std::shared_ptr<git::Repository> repository);
void updateMapFileStatus();
void onMapEvent(IMap::MapEvent ev);
Expand Down

0 comments on commit f51e893

Please sign in to comment.