Skip to content
14 changes: 14 additions & 0 deletions src/Command/PullRequest/PullRequestMergeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -79,6 +81,16 @@ protected function configure()

<info>$ gush %command.name% --fast-forward 12</info>

If you want to perform an automatic rebase against the base branch before merging, the <comment>--rebase</comment> option can be used
in order to try that operation:

<info>$ gush %command.name% --rebase 12</info>

A synchronization check against the base branch can be done before the merge, passing the <comment>--ensure-sync</comment> option; so
if this check fails, the operation will be aborted:

<info>$ gush %command.name% --ensure-sync 12</info>

After the pull request is merged, you can give a pat on the back to its author using the <comment>--pat</comment>.
This option accepts the name of any configured pat's name:

Expand Down Expand Up @@ -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'));
Expand Down
63 changes: 28 additions & 35 deletions src/Helper/GitHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Gush\Helper;

use Gush\Exception\CannotSquashMultipleAuthors;
use Gush\Exception\MergeWorkflowException;
use Gush\Exception\UserException;
use Gush\Exception\WorkingTreeIsNotReady;
use Gush\Operation\RemoteMergeOperation;
Expand Down Expand Up @@ -342,7 +343,7 @@ public function clearTempBranches()
*/
public function createRemoteMergeOperation()
{
return new RemoteMergeOperation($this, $this->filesystemHelper);
return new RemoteMergeOperation($this, $this->filesystemHelper, $this->processHelper);
}

/**
Expand Down Expand Up @@ -386,7 +387,7 @@ public function mergeBranch($base, $sourceBranch, $commitMessage, $fastForward =
'allow_failures' => false,
],
[
'line' => ['git', 'commit', '-F', $tmpName],
'line' => ['git', 'commit', '--file', $tmpName],
'allow_failures' => false,
],
]
Expand Down Expand Up @@ -433,7 +434,7 @@ public function mergeBranchWithLog($base, $sourceBranch, $commitMessage, $source
$tmpName = $this->filesystemHelper->newTempFilename();
file_put_contents($tmpName, $commitMessage);

$this->processHelper->runCommand(['git', 'commit', '-F', $tmpName]);
$this->processHelper->runCommand(['git', 'commit', '--file', $tmpName]);

return trim($this->processHelper->runCommand('git rev-parse HEAD'));
}
Expand All @@ -443,15 +444,7 @@ public function addNotes($notes, $commitHash, $ref)
$tmpName = $this->filesystemHelper->newTempFilename();
file_put_contents($tmpName, $notes);

$commands = [
'git',
'notes',
'--ref='.$ref,
'add',
'-F',
$tmpName,
$commitHash,
];
$commands = ['git', 'notes', '--ref', $ref, 'add', '--file', $tmpName, $commitHash];

$this->processHelper->runCommand($commands, true);
}
Expand Down Expand Up @@ -596,21 +589,19 @@ public function squashCommits($base, $branchName, $ignoreMultipleAuthors = false
// Get commits only in the branch but not in base (in reverse order)
// we can't use --max-count here because that is applied before the reversing!
//
// using git-log works better then finding the fork-point with git-merge-base
// using git-log works better than finding the fork-point with git-merge-base
// because this protects against edge cases were there is no valid fork-point

$firstCommitHash = StringUtil::splitLines($this->processHelper->runCommand(
[
'git',
'--no-pager',
'log',
'--oneline',
'--no-color',
'--format=%H',
'--reverse',
$base.'..'.$branchName,
]
))[0];
$firstCommitHash = StringUtil::splitLines($this->processHelper->runCommand([
'git',
'--no-pager',
'log',
'--oneline',
'--no-color',
'--format=%H',
'--reverse',
$base.'..'.$branchName,
]))[0];

// 0=author anything higher then 0 is the full body
$commitData = StringUtil::splitLines(
Expand All @@ -631,13 +622,7 @@ public function squashCommits($base, $branchName, $ignoreMultipleAuthors = false
$message = implode("\n", $commitData);

$this->reset($base);
$this->commit(
$message,
[
'a',
'-author' => $author,
]
);
$this->commit($message, ['all', 'author' => $author]);
}

public function syncWithRemote($remote, $branchName = null)
Expand Down Expand Up @@ -671,17 +656,25 @@ public function commit($message, array $options = [])

foreach ($options as $option => $value) {
if (is_int($option)) {
$params[] = '-'.$value;
if (1 === strlen($value)) {
$params[] = '-'.$value;
} else {
$params[] = '--'.$value;
}
} else {
$params[] = '-'.$option;
if (1 === strlen($option)) {
$params[] = '-'.$option;
} else {
$params[] = '--'.$option;
}
$params[] = $value;
}
}

$tmpName = $this->filesystemHelper->newTempFilename();
file_put_contents($tmpName, $message);

$this->processHelper->runCommand(array_merge(['git', 'commit', '-F', $tmpName], $params));
$this->processHelper->runCommand(array_merge(['git', 'commit', '--file', $tmpName], $params));
}

/**
Expand Down
41 changes: 40 additions & 1 deletion src/Operation/RemoteMergeOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@

use Gush\Helper\FilesystemHelper;
use Gush\Helper\GitHelper;
use Gush\Helper\ProcessHelper;

class RemoteMergeOperation
{
private $gitHelper;
private $filesystemHelper;
private $processHelper;

private $sourceBranch;
private $sourceRemote;
Expand All @@ -31,11 +33,14 @@ class RemoteMergeOperation
private $performed = false;
private $fastForward = false;
private $withLog = false;
private $rebase = false;
private $guardSync = false;

public function __construct(GitHelper $gitHelper, FilesystemHelper $filesystemHelper)
public function __construct(GitHelper $gitHelper, FilesystemHelper $filesystemHelper, ProcessHelper $processHelper)
{
$this->gitHelper = $gitHelper;
$this->filesystemHelper = $filesystemHelper;
$this->processHelper = $processHelper;
}

public function setSource($remote, $branch)
Expand Down Expand Up @@ -80,6 +85,8 @@ public function setMergeMessage($message, $withLog = false)
public function useFastForward($fastForward = true)
{
$this->fastForward = (bool) $fastForward;

return $this;
}

public function performMerge()
Expand Down Expand Up @@ -143,6 +150,20 @@ public function pushToRemote()
$this->gitHelper->pushToRemote($this->targetRemote, $target);
}

public function rebase(bool $rebase = false)
{
$this->rebase = $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;
Expand Down Expand Up @@ -170,6 +191,24 @@ private function createSourceBranch()
$this->targetBranch = $this->switchBase;
}

$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);
}
} elseif ($this->guardSync) {
throw new MergeWorkflowException(sprintf('Failed while trying to perform merge against "%s", history is out of sync.', $this->targetBase));
}
}

if ($this->squash) {
$this->gitHelper->squashCommits($this->targetBase, $sourceBranch, $this->forceSquash);
}
Expand Down
4 changes: 3 additions & 1 deletion tests/Command/PullRequest/PullRequestMergeCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion tests/Helper/GitHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ public function merges_remote_branch_in_clean_wc()
'allow_failures' => false,
],
[
'line' => ['git', 'commit', '-F', $tmpName],
'line' => ['git', 'commit', '--file', $tmpName],
'allow_failures' => false,
],
]
Expand Down