diff --git a/plugins/vcs/Algorithm.h b/plugins/vcs/Algorithm.h index 1cebccca2f..7939cbab91 100644 --- a/plugins/vcs/Algorithm.h +++ b/plugins/vcs/Algorithm.h @@ -135,6 +135,12 @@ inline void syncWithRemote(const std::shared_ptr& repository) repository->pushToTrackedRemote(); return; } + + if (status.strategy == RequiredMergeStrategy::FastForward) + { + repository->fastForwardToTrackedRemote(); + return; + } if (!repository->isReadyForMerge()) { diff --git a/plugins/vcs/Reference.h b/plugins/vcs/Reference.h index d12feb6fbc..0dffca186d 100644 --- a/plugins/vcs/Reference.h +++ b/plugins/vcs/Reference.h @@ -3,6 +3,7 @@ #include #include #include +#include "GitException.h" namespace vcs { @@ -62,6 +63,20 @@ class Reference final return upstream != nullptr ? std::make_shared(upstream) : Ptr(); } + // Create a new reference with the same name as the given reference but a + // different OID target. The new reference will be written to disk, overwriting the given reference. + void setTarget(git_oid* oid) + { + git_reference* newTargetRef; + auto error = git_reference_set_target(&newTargetRef, _reference, oid, "Reference set to new target by DarkRadiant"); + GitException::ThrowOnError(error); + + // Swap the wrapped reference pointer, release the old one + git_reference_free(_reference); + + _reference = newTargetRef; + } + ~Reference() { git_reference_free(_reference); diff --git a/plugins/vcs/Repository.cpp b/plugins/vcs/Repository.cpp index d8a5a5c5d5..8155b95582 100644 --- a/plugins/vcs/Repository.cpp +++ b/plugins/vcs/Repository.cpp @@ -150,6 +150,45 @@ void Repository::pushToTrackedRemote() remote->push(*getHead()); // getHead will succeed because getTrackedRemote did } +void Repository::fastForwardToTrackedRemote() +{ + auto head = getHead(); + if (!head) throw GitException("Could not retrieve HEAD reference from repository"); + + auto upstream = head->getUpstream(); + if (!upstream) throw GitException("No tracked remote branch configured"); + + // Lookup the target object + git_oid targetOid; + git_reference_name_to_id(&targetOid, _repository, upstream->getName().c_str()); + + git_object* target; + auto error = git_object_lookup(&target, _repository, &targetOid, GIT_OBJECT_COMMIT); + GitException::ThrowOnError(error); + + rMessage() << "Fast-fowarding " << head->getName() << " to upstream " << upstream->getName() << std::endl; + + try + { + // Checkout the result so the workdir is in the expected state + git_checkout_options checkoutOptions = GIT_CHECKOUT_OPTIONS_INIT; + checkoutOptions.checkout_strategy = GIT_CHECKOUT_SAFE; + + error = git_checkout_tree(_repository, target, &checkoutOptions); + GitException::ThrowOnError(error); + + // Move the target reference to the target OID + head->setTarget(&targetOid); + + rMessage() << "Fast-foward done, " << head->getName() << " is now at " << Reference::OidToString(&targetOid) << std::endl; + } + catch (const GitException& ex) + { + git_object_free(target); + throw ex; + } +} + RefSyncStatus Repository::getSyncStatusOfBranch(const Reference& reference) { RefSyncStatus status; diff --git a/plugins/vcs/Repository.h b/plugins/vcs/Repository.h index 3b2287ab72..f77a1af146 100644 --- a/plugins/vcs/Repository.h +++ b/plugins/vcs/Repository.h @@ -51,6 +51,9 @@ class Repository final // Performs a fetch from the remote the current branch is tracking void fetchFromTrackedRemote(); + // Fast-forwards the current head to the tracked remote branch + void fastForwardToTrackedRemote(); + // Pushes the current head to its tracked remote branch void pushToTrackedRemote(); diff --git a/plugins/vcs/ui/VcsStatus.cpp b/plugins/vcs/ui/VcsStatus.cpp index 64d0e999ae..95750235bb 100644 --- a/plugins/vcs/ui/VcsStatus.cpp +++ b/plugins/vcs/ui/VcsStatus.cpp @@ -18,6 +18,7 @@ #include "../GitModule.h" #include "../Diff.h" #include "../GitException.h" +#include "../Algorithm.h" namespace vcs { @@ -232,7 +233,7 @@ void VcsStatus::performSync(std::shared_ptr repository) { try { - repository->pushToTrackedRemote(); + syncWithRemote(repository); setRemoteStatus(git::analyseRemoteStatus(repository)); } catch (git::GitException& ex)