Skip to content

Commit

Permalink
#5662: Work on completing the merge operation and creating the merge …
Browse files Browse the repository at this point in the history
…commit. This is not quite working yet.
  • Loading branch information
codereader committed Jul 21, 2021
1 parent d291a24 commit 81c2835
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 17 deletions.
64 changes: 50 additions & 14 deletions plugins/vcs/Algorithm.h
Expand Up @@ -11,6 +11,7 @@
#include "VersionControlLib.h"
#include "command/ExecutionFailure.h"
#include "wxutil/dialog/MessageBox.h"
#include "ui/CommitDialog.h"
#include <git2.h>

namespace vcs
Expand Down Expand Up @@ -122,6 +123,52 @@ inline RemoteStatus analyseRemoteStatus(const std::shared_ptr<Repository>& repos
RequiredMergeStrategy::MergeMap };
}

inline void tryToFinishMerge(const std::shared_ptr<Repository>& repository)
{
auto mapPath = repository->getRepositoryRelativePath(GlobalMapModule().getMapName());

auto index = repository->getIndex();

if (index->hasConflicts())
{
// Remove the conflict state from the map
if (!mapPath.empty() && index->fileIsConflicted(mapPath))
{
index->resolveByUsingOurs(mapPath);
index->write();
}

// If the index still has conflicts, notify the user
if (index->hasConflicts())
{
wxutil::Messagebox::Show(_("Conflicts"),
_("There are still unresolved conflicts in the repository.\nPlease use your Git client to resolve them and try again."),
::ui::IDialog::MessageType::MESSAGE_CONFIRM);
return;
}
}

auto head = repository->getHead();
if (!head) throw git::GitException("Cannot resolve repository HEAD");

auto upstream = head->getUpstream();
if (!upstream) throw git::GitException("Cannot resolve upstream ref from HEAD");

// We need the commit metadata to be valid
git::CommitMetadata metadata;

metadata.name = repository->getConfigValue("user.name");
metadata.email = repository->getConfigValue("user.email");

metadata = ui::CommitDialog::RunDialog(metadata);

if (metadata.isValid())
{
repository->createCommit(metadata, upstream);
repository->cleanupState();
}
}

inline void syncWithRemote(const std::shared_ptr<Repository>& repository)
{
if (repository->mergeIsInProgress())
Expand Down Expand Up @@ -206,7 +253,7 @@ inline void syncWithRemote(const std::shared_ptr<Repository>& repository)
error = git_merge(repository->_get(), mergeHeads.data(), mergeHeads.size(), &mergeOptions, &checkoutOptions);
GitException::ThrowOnError(error);

// At this point, check if the loaded map is affected by the merge
// Check if the loaded map is affected by the merge
if (status.strategy == RequiredMergeStrategy::MergeMap)
{
auto mergeBase = repository->findMergeBase(*repository->getHead(), *upstream);
Expand All @@ -224,22 +271,11 @@ inline void syncWithRemote(const std::shared_ptr<Repository>& repository)
throw GitException(ex.what());
}

// TODO: save this state and continue to commit and cleanup later
// Wait until the user finishes or aborts the merge
return;
}

auto index = repository->getIndex();

if (index->hasConflicts())
{
// Handle conflicts -notify user?
}
else
{
//create_merge_commit(repository->_get(), index, &mergeOptions);
}

git_repository_state_cleanup(repository->_get());
tryToFinishMerge(repository);
}
catch (const GitException& ex)
{
Expand Down
16 changes: 16 additions & 0 deletions plugins/vcs/Index.cpp
Expand Up @@ -66,6 +66,22 @@ void Index::write()
GitException::ThrowOnError(error);
}

void Index::resolveByUsingOurs(const std::string& relativePath)
{
auto error = git_index_add_bypath(_index, relativePath.c_str());
GitException::ThrowOnError(error);
}

bool Index::fileIsConflicted(const std::string& relativePath)
{
const git_index_entry* ancestor = nullptr;
const git_index_entry* ours = nullptr;
const git_index_entry* theirs = nullptr;

auto error = git_index_conflict_get(&ancestor, &ours, &theirs, _index, relativePath.c_str());
return error == 0;
}

bool Index::hasConflicts()
{
return git_index_has_conflicts(_index);
Expand Down
5 changes: 5 additions & 0 deletions plugins/vcs/Index.h
@@ -1,6 +1,7 @@
#pragma once

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

namespace vcs
Expand Down Expand Up @@ -37,6 +38,10 @@ class Index final
std::shared_ptr<Tree> writeTree(Repository& repository);

bool hasConflicts();

bool fileIsConflicted(const std::string& relativePath);

void resolveByUsingOurs(const std::string& relativePath);
};

}
Expand Down
31 changes: 28 additions & 3 deletions plugins/vcs/Repository.cpp
Expand Up @@ -306,6 +306,11 @@ std::shared_ptr<Tree> Repository::getTreeByRevision(const std::string& revision)
}

void Repository::createCommit(const CommitMetadata& metadata)
{
createCommit(metadata, Reference::Ptr());
}

void Repository::createCommit(const CommitMetadata& metadata, const Reference::Ptr& additionalParent)
{
auto head = getHead();
auto index = getIndex();
Expand All @@ -322,24 +327,38 @@ void Repository::createCommit(const CommitMetadata& metadata)
auto tree = index->writeTree(*this);

git_oid headOid;
git_reference_name_to_id(&headOid, _repository, head->getName().c_str());
error = git_reference_name_to_id(&headOid, _repository, head->getName().c_str());
GitException::ThrowOnError(error);

auto parentCommit = Commit::LookupFromOid(_repository, &headOid);

std::vector<const git_commit*> parentCommits;
parentCommits.push_back(parentCommit->_get());

// Check if we have an additional parent
if (additionalParent)
{
git_oid parentOid;
auto error = git_reference_name_to_id(&parentOid, _repository, additionalParent->getName().c_str());
GitException::ThrowOnError(error);

auto additionalParentCommit = Commit::LookupFromOid(_repository, &parentOid);

parentCommits.push_back(additionalParentCommit->_get());
}

git_oid commitOid;
error = git_commit_create(&commitOid,
_repository, head->getName().c_str(),
signature, signature,
nullptr, metadata.message.c_str(),
tree->_get(),
tree->_get(),
parentCommits.size(), parentCommits.data());
GitException::ThrowOnError(error);

index->write();

rMessage() << "Commit created with " << Reference::OidToString(&commitOid) << std::endl;
rMessage() << "Commit created: " << Reference::OidToString(&commitOid) << std::endl;
}

std::string Repository::getConfigValue(const std::string& key)
Expand Down Expand Up @@ -368,6 +387,12 @@ std::string Repository::getConfigValue(const std::string& key)
}
}

void Repository::cleanupState()
{
auto error = git_repository_state_cleanup(_repository);
GitException::ThrowOnError(error);
}

bool Repository::isReadyForMerge()
{
auto state = git_repository_state(_repository);
Expand Down
7 changes: 7 additions & 0 deletions plugins/vcs/Repository.h
Expand Up @@ -81,10 +81,17 @@ class Repository final

std::shared_ptr<Tree> getTreeByRevision(const std::string& revision);

// Create a commit with the current repository HEAD as only parent
void createCommit(const CommitMetadata& metadata);

// Create a commit with the currrent repository HEAD and the given ref as parent
// It's valid to pass an empty additionalParent if no second parent is needed
void createCommit(const CommitMetadata& metadata, const Reference::Ptr& additionalParent);

std::string getConfigValue(const std::string& key);

void cleanupState();

// Creates a new instance of this repository, not sharing any libgit2 handles with the original
std::shared_ptr<Repository> clone();

Expand Down
48 changes: 48 additions & 0 deletions plugins/vcs/ui/VcsStatus.cpp
Expand Up @@ -162,6 +162,54 @@ void VcsStatus::onMapEvent(IMap::MapEvent ev)
analyseRemoteStatus(_repository);
}
}

// Only when saving: check whether a merge is to be completed
if (ev == IMap::MapSaved && _repository && _repository->mergeIsInProgress())
{
auto mapPath = _repository->getRepositoryRelativePath(GlobalMapModule().getMapName());
auto index = _repository->getIndex();

// Remove the conflict state of the map file after save
if (!mapPath.empty() && index->fileIsConflicted(mapPath))
{
index->resolveByUsingOurs(mapPath);
index->write();
}

if (wxutil::Messagebox::Show(_("Complete Merge Operation?"),
_("Map has been saved. Do you want to complete the ongoing merge operation using this state?"),
::ui::IDialog::MessageType::MESSAGE_ASK) == ::ui::IDialog::RESULT_YES)
{
try
{
tryToFinishMerge(_repository);
}
catch (git::GitException& ex)
{
wxutil::Messagebox::ShowError(ex.what());
}
}
}

if (ev == IMap::MapMergeOperationAborted && _repository && _repository->mergeIsInProgress())
{
// Ask the user whether to cancel the git merge status
if (wxutil::Messagebox::Show(_("Cancel Merge Operation?"),
_("You've aborted the map merge. Do you want to abort the ongoing git merge operation too?\n"
"This will reset the repository to the state it had before the merge was started."),
::ui::IDialog::MessageType::MESSAGE_ASK) == ::ui::IDialog::RESULT_YES)
{
// TODO
}
}
else if (ev == IMap::MapMergeOperationFinished && _repository && _repository->mergeIsInProgress())
{
wxutil::Messagebox::Show(_("Save the map when done merging"),
_("Now that the merge is finished, please save the map such that the git merge operation can be completed."),
::ui::IDialog::MessageType::MESSAGE_CONFIRM);

// Now wait for the merge to complete (MapSaved event)
}
}

void VcsStatus::startFetchTask()
Expand Down

0 comments on commit 81c2835

Please sign in to comment.