diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 3c6342849..7cdb40373 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -235,17 +235,22 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface return; } + $candidateBranches = $matches[1]; // use the first match as branch name for now - $branch = $matches[1][0]; + $branch = $candidateBranches[0]; $unpushedChanges = null; // do two passes, as if we find anything we want to fetch and then re-try for ($i = 0; $i <= 1; $i++) { - // try to find the a matching branch name in the composer remote - foreach ($matches[1] as $candidate) { - if (preg_match('{^[a-f0-9]+ refs/remotes/((?:composer|origin)/'.preg_quote($candidate).')$}mi', $refs, $match)) { - $branch = $candidate; - $remoteBranch = $match[1]; + $remoteBranches = array(); + + // try to find matching branch names in remote repos + foreach ($candidateBranches as $candidate) { + if (preg_match_all('{^[a-f0-9]+ refs/remotes/((?:[^/]+)/'.preg_quote($candidate).')$}mi', $refs, $matches)) { + foreach ($matches[1] as $match) { + $branch = $candidate; + $remoteBranches[] = $match; + } break; } } @@ -253,21 +258,34 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface // if it doesn't exist, then we assume it is an unpushed branch // this is bad as we have no reference point to do a diff so we just bail listing // the branch as being unpushed - if (!isset($remoteBranch)) { + if (!$remoteBranches) { $unpushedChanges = 'Branch ' . $branch . ' could not be found on the origin remote and appears to be unpushed'; } else { - $command = sprintf('git diff --name-status %s...%s --', $remoteBranch, $branch); - if (0 !== $this->process->execute($command, $output, $path)) { - throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); - } + foreach ($remoteBranches as $remoteBranch) { + $command = sprintf('git diff --name-status %s...%s --', $remoteBranch, $branch); + if (0 !== $this->process->execute($command, $output, $path)) { + throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); + } - $unpushedChanges = trim($output) ?: null; + $output = trim($output); + // keep the shortest diff from all remote branches we compare against + if ($unpushedChanges === null || strlen($output) < strlen($unpushedChanges)) { + $unpushedChanges = $output ?: null; + } + } } - // first pass and we found unpushed changes, fetch from both remotes to make sure we have up to date + // first pass and we found unpushed changes, fetch from all remotes to make sure we have up to date // remotes and then try again as outdated remotes can sometimes cause false-positives if ($unpushedChanges && $i === 0) { - $this->process->execute('git fetch composer && git fetch origin', $output, $path); + $this->process->execute('git fetch --all', $output, $path); + + // update list of refs after fetching + $command = 'git show-ref --head -d'; + if (0 !== $this->process->execute($command, $output, $path)) { + throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput()); + } + $refs = trim($output); } // abort after first pass if we didn't find anything