Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. GitDownloader.php

function GitDownloader::getUnpushedChanges

Overrides DvcsDownloaderInterface::getUnpushedChanges

1 call to GitDownloader::getUnpushedChanges()
GitDownloader::cleanChanges in vendor/composer/composer/src/Composer/Downloader/GitDownloader.php
@inheritDoc

File

vendor/composer/composer/src/Composer/Downloader/GitDownloader.php, line 231

Class

GitDownloader
@author Jordi Boggiano <j.boggiano@seld.be>

Namespace

Composer\Downloader

Code

public function getUnpushedChanges(PackageInterface $package, string $path) : ?string {
    GitUtil::cleanEnv();
    $path = $this->normalizePath($path);
    if (!$this->hasMetadataRepository($path)) {
        return null;
    }
    $command = [
        'git',
        'show-ref',
        '--head',
        '-d',
    ];
    if (0 !== $this->process
        ->execute($command, $output, $path)) {
        throw new \RuntimeException('Failed to execute ' . implode(' ', $command) . "\n\n" . $this->process
            ->getErrorOutput());
    }
    $refs = trim($output);
    if (!Preg::isMatchStrictGroups('{^([a-f0-9]+) HEAD$}mi', $refs, $match)) {
        // could not match the HEAD for some reason
        return null;
    }
    $headRef = $match[1];
    if (!Preg::isMatchAllStrictGroups('{^' . preg_quote($headRef) . ' refs/heads/(.+)$}mi', $refs, $matches)) {
        // not on a branch, we are either on a not-modified tag or some sort of detached head, so skip this
        return null;
    }
    $candidateBranches = $matches[1];
    // use the first match as branch name for now
    $branch = $candidateBranches[0];
    $unpushedChanges = null;
    $branchNotFoundError = false;
    // do two passes, as if we find anything we want to fetch and then re-try
    for ($i = 0; $i <= 1; $i++) {
        $remoteBranches = [];
        // try to find matching branch names in remote repos
        foreach ($candidateBranches as $candidate) {
            if (Preg::isMatchAllStrictGroups('{^[a-f0-9]+ refs/remotes/((?:[^/]+)/' . preg_quote($candidate) . ')$}mi', $refs, $matches)) {
                foreach ($matches[1] as $match) {
                    $branch = $candidate;
                    $remoteBranches[] = $match;
                }
                break;
            }
        }
        // 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 (count($remoteBranches) === 0) {
            $unpushedChanges = 'Branch ' . $branch . ' could not be found on any remote and appears to be unpushed';
            $branchNotFoundError = true;
        }
        else {
            // if first iteration found no remote branch but it has now found some, reset $unpushedChanges
            // so we get the real diff output no matter its length
            if ($branchNotFoundError) {
                $unpushedChanges = null;
            }
            foreach ($remoteBranches as $remoteBranch) {
                $command = [
                    'git',
                    'diff',
                    '--name-status',
                    $remoteBranch . '...' . $branch,
                    '--',
                ];
                if (0 !== $this->process
                    ->execute($command, $output, $path)) {
                    throw new \RuntimeException('Failed to execute ' . implode(' ', $command) . "\n\n" . $this->process
                        ->getErrorOutput());
                }
                $output = trim($output);
                // keep the shortest diff from all remote branches we compare against
                if ($unpushedChanges === null || strlen($output) < strlen($unpushedChanges)) {
                    $unpushedChanges = $output;
                }
            }
        }
        // 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',
                '--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 ' . implode(' ', $command) . "\n\n" . $this->process
                    ->getErrorOutput());
            }
            $refs = trim($output);
        }
        // abort after first pass if we didn't find anything
        if (!$unpushedChanges) {
            break;
        }
    }
    return $unpushedChanges;
}

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal