diff --git a/src/Command/PullRequest/PullRequestMergeCommand.php b/src/Command/PullRequest/PullRequestMergeCommand.php
index aefaf354..69253302 100644
--- a/src/Command/PullRequest/PullRequestMergeCommand.php
+++ b/src/Command/PullRequest/PullRequestMergeCommand.php
@@ -40,6 +40,8 @@ protected function configure()
->addOption('fast-forward', null, InputOption::VALUE_NONE, 'Merge pull-request using fast forward (no merge commit will be created)')
->addOption('squash', null, InputOption::VALUE_NONE, 'Squash the PR before merging')
->addOption('force-squash', null, InputOption::VALUE_NONE, 'Force squashing the PR, even if there are multiple authors (this will implicitly use --squash)')
+ ->addOption('rebase', null, InputOption::VALUE_NONE, 'Rebase the PR before merging')
+ ->addOption('ensure-sync', null, InputOption::VALUE_NONE, 'Ensure that the pull request history is up to date before merging')
->addOption('switch', null, InputOption::VALUE_REQUIRED, 'Switch the base of the pull request before merging')
->addOption('pat', null, InputOption::VALUE_REQUIRED, 'Give the PR\'s author a pat on the back after the merge')
->setHelp(
@@ -79,6 +81,16 @@ protected function configure()
$ gush %command.name% --fast-forward 12
+If you want to perform an automatic rebase against the base branch before merging, the --rebase option can be used
+in order to try that operation:
+
+ $ gush %command.name% --rebase 12
+
+A synchronization check against the base branch can be done before the merge, passing the --ensure-sync option; so
+if this check fails, the operation will be aborted:
+
+ $ gush %command.name% --ensure-sync 12
+
After the pull request is merged, you can give a pat on the back to its author using the --pat.
This option accepts the name of any configured pat's name:
@@ -173,6 +185,8 @@ protected function execute(InputInterface $input, OutputInterface $output)
$mergeOperation->setTarget($targetRemote, $targetBranch);
$mergeOperation->setSource($sourceRemote, $sourceBranch);
$mergeOperation->squashCommits($squash, $input->getOption('force-squash'));
+ $mergeOperation->guardSync($input->getOption('ensure-sync'));
+ $mergeOperation->rebase($input->getOption('rebase'));
$mergeOperation->switchBase($input->getOption('switch'));
$mergeOperation->setMergeMessage($messageCallback);
$mergeOperation->useFastForward($input->getOption('fast-forward'));
diff --git a/src/Helper/GitHelper.php b/src/Helper/GitHelper.php
index cd866b18..a7906886 100644
--- a/src/Helper/GitHelper.php
+++ b/src/Helper/GitHelper.php
@@ -615,13 +615,6 @@ public function squashCommits($base, $branchName, $ignoreMultipleAuthors = false
$base.'..'.$branchName,
]))[0];
- $currentBaseHeadCommit = $this->processHelper->runCommand(['git', 'rev-parse', $base]);
- $lastKnownCommonCommit = $this->processHelper->runCommand(['git', 'merge-base', '--fork-point', $base, $branchName]);
-
- if ($currentBaseHeadCommit !== $lastKnownCommonCommit) {
- throw new MergeWorkflowException(sprintf('Failed while trying to perform merge against "%s", history is out of sync.', $base));
- }
-
// 0=author anything higher then 0 is the full body
$commitData = StringUtil::splitLines(
$this->processHelper->runCommand(
diff --git a/src/Operation/RemoteMergeOperation.php b/src/Operation/RemoteMergeOperation.php
index 2bc4969b..b85ad677 100644
--- a/src/Operation/RemoteMergeOperation.php
+++ b/src/Operation/RemoteMergeOperation.php
@@ -31,6 +31,8 @@ class RemoteMergeOperation
private $performed = false;
private $fastForward = false;
private $withLog = false;
+ private $rebase = false;
+ private $guardSync = false;
public function __construct(GitHelper $gitHelper, FilesystemHelper $filesystemHelper)
{
@@ -80,6 +82,8 @@ public function setMergeMessage($message, $withLog = false)
public function useFastForward($fastForward = true)
{
$this->fastForward = (bool) $fastForward;
+
+ return $this;
}
public function performMerge()
@@ -143,6 +147,21 @@ public function pushToRemote()
$this->gitHelper->pushToRemote($this->targetRemote, $target);
}
+ public function rebase(bool $rebase = false)
+ {
+ $this->rebase = $rebase;
+ $this->guardSync = !$rebase;
+
+ return $this;
+ }
+
+ public function guardSync(bool $guardSync = false)
+ {
+ $this->guardSync = $guardSync;
+
+ return $this;
+ }
+
private function createBaseBranch()
{
$targetBranch = null !== $this->switchBase ? $this->switchBase : $this->targetBranch;
@@ -171,7 +190,31 @@ private function createSourceBranch()
}
if ($this->squash) {
- $this->gitHelper->squashCommits($this->targetBase, $sourceBranch, $this->forceSquash);
+ $this->gitHelper->squashCommits($this->targetBase, $sourceBranch, $this->forceSquash, $this->guardSync);
+ }
+
+ $currentBaseHeadCommit = $this->processHelper->runCommand(['git', 'rev-parse', $this->targetBase]);
+ $lastKnownCommonCommit = $this->processHelper->runCommand(['git', 'merge-base', '--fork-point', $this->targetBase, $sourceBranch]);
+
+ if ($currentBaseHeadCommit !== $lastKnownCommonCommit) {
+ if ($this->rebase) {
+ try {
+ $this->processHelper->runCommand(['git', 'pull', '--rebase', $this->targetBase]);
+ } catch (\Exception $e) {
+ // Error, abort the rebase operation
+ $this->processHelper->runCommand(['git', 'rebase', '--abort'], true);
+
+ throw new MergeWorkflowException(sprintf('Git rebase failed while trying to synchronize history against "%s".', $this->targetBase), 0, $e);
+ }
+
+ // Retrieve the commits again
+ $currentBaseHeadCommit = $this->processHelper->runCommand(['git', 'rev-parse', $this->targetBase]);
+ $lastKnownCommonCommit = $this->processHelper->runCommand(['git', 'merge-base', '--fork-point', $this->targetBase, $sourceBranch]);
+ }
+
+ if ($this->guardSync && $currentBaseHeadCommit !== $lastKnownCommonCommit) {
+ throw new MergeWorkflowException(sprintf('Failed while trying to perform merge against "%s", history is out of sync.', $this->targetBase));
+ }
}
// Allow a callback to allow late commits list composition
diff --git a/tests/Command/PullRequest/PullRequestMergeCommandTest.php b/tests/Command/PullRequest/PullRequestMergeCommandTest.php
index aef6b1a2..7761135f 100644
--- a/tests/Command/PullRequest/PullRequestMergeCommandTest.php
+++ b/tests/Command/PullRequest/PullRequestMergeCommandTest.php
@@ -455,7 +455,7 @@ protected function getGitConfigHelper($notes = true)
return $helper;
}
- private function getLocalGitHelper($message = null, $squash = false, $forceSquash = false, $switch = null, $withComments = true, $fastForward = false)
+ private function getLocalGitHelper($message = null, $squash = false, $forceSquash = false, $switch = null, $withComments = true, $fastForward = false, $guardSync = false, $rebase = false)
{
$helper = parent::getGitHelper();
@@ -470,6 +470,8 @@ private function getLocalGitHelper($message = null, $squash = false, $forceSquas
$mergeOperation->setTarget('gushphp', 'base_ref')->shouldBeCalled();
$mergeOperation->setSource('cordoval', 'head_ref')->shouldBeCalled();
$mergeOperation->squashCommits($squash, $forceSquash)->shouldBeCalled();
+ $mergeOperation->guardSync($guardSync)->shouldBeCalled();
+ $mergeOperation->rebase($rebase)->shouldBeCalled();
$mergeOperation->switchBase($switch)->shouldBeCalled();
$mergeOperation->useFastForward($fastForward)->shouldBeCalled();
$mergeOperation->setMergeMessage(