通道“服务器上的git”通过PHP调用


Channel `git on the server` calls through PHP

是否可以通过PHP传输git pullgit push命令?

我的意思是,我会将https://example.com/projectname?credentials=xxx设置为git origin,然后当我执行pullpush时,PHP脚本会分析凭据,如果它们正常,则将git发送的原始命令发送到我的服务器上的git-reo文件夹,然后将响应发送回用户?

git-http-backend可以用来实现这一点吗?或者还有其他方法吗?

我终于让它适用于拉相关命令,但不适用于推送命令

static $basegitdir = '/path/to/where/bare/git/repos/live';
/**
 * A PHP wrapper for git-http-backend
 */
static function httpBackend() {
    $packagename = 'gitrepo';
    $gitdir = self::$basegitdir . "{$packagename}.git";
    $gitcoredir = '/usr/lib/git-core'; // True for Ubuntu systems
    try {
        // In my implementation, func_get_args will return e.g. ['info', 'refs'], basically, all of the stuff git appends to the remote url
        $arguments = func_get_args();
        // Remove the first argument
        array_unshift($arguments, "{$packagename}.git");
        // Will resolve to something like '/repo.git/info/refs'
        $path = '/' . implode('/', $arguments);
        $service = filter_input(INPUT_GET, 'service');
        if ($service) {
            $service = "?service={$service}";
        }
        $res = self::proc_open("{$gitcoredir}/git-http-backend", [], $gitdir, true, [
            'PATH' => filter_input(INPUT_SERVER, 'PATH'),
            'GIT_PROJECT_ROOT' => self::$basegitdir,
            'GIT_HTTP_EXPORT_ALL' => 1,
            // PATH_INFO <MUST> evaluate to something along the lines of '/repo.git/info/refs'. Note, basegitdir has been 
            //  dropped from the beginning of the path. This is because Git joins GIT_PROJECT_ROOT and PATH_INFO. Also note the lack of a trailing slash after refs
            'PATH_INFO' => $path,
            'REMOTE_ADDR' => filter_input(INPUT_SERVER, 'REMOTE_ADDR'),
            // QUERY_STRING MUST evaluate to something along the lines of '/repo.git/info/refs/?service=service', note the trailing slash after refs
            'QUERY_STRING' => "{$path}/{$service}",
            'REQUEST_METHOD' => filter_input(INPUT_SERVER, 'REQUEST_METHOD'),
        ]);
    } catch ('Exception $ex) {
        // Log "Exception: {$ex->getMessage()}";
        exit;
    }
    $resbits = explode("'n", $res);
    foreach ($resbits as $index => $header) {
        if ($header && strpos($header, ':') !== false) {
            // Headers
            header($header);
            unset($resbits[$index]);
        } else {
            // First blank line is the space between the headers and the start of the response from Git
            break;
        }
    }
    echo ltrim(implode("'n", $resbits));
    exit;
}
/**
 * A wrapper for the proc_open functions
 * @param string $command The command to execute
 * @param array $arguments Responses to bash
 * @param string $cwd The current working directory
 * @param boolean $blocking False to make requests asynchronous
 * @param array $env Environment variables
 * @return string The output
 * @throws 'Exception
 */
static function proc_open($command, array $arguments = [], $cwd = rootdir, $blocking = true, array $env = null) {
    $pipes = [];
    $descriptorspec = array(
       array('pipe', 'r'),  // STDIN
       array('pipe', 'w'),  // STDOUT
       array('pipe', 'w'),  // STDERR
    );
    $process = proc_open(trim($command), $descriptorspec, $pipes, $cwd, $env);
    stream_set_blocking($pipes[1], (int) $blocking);
    stream_set_blocking($pipes[2], (int) 1);
    foreach ($arguments as $arg) {
        // Write each of the supplied arguments to STDIN and ensure that it finishes with one trailing 
        fwrite($pipes[0], (preg_match("/'n(:?'s+)?$/", $arg) ? $arg : "{$arg}'n"));
    }
    $response = stream_get_contents($pipes[1]);
    $error = stream_get_contents($pipes[2]);
    if ($error) {
        throw new 'Exception($error);
    }
    // Make sure that each pipe is closed to prevent a lockout
    foreach ($pipes as $pipe) {
        fclose($pipe);
    }
    proc_close($process);
    return $response;
}