How to Perform Git Operations with PHP

Advertisement

Advertisement

Git operations can be performed programmatically with PHP using the libgit2 library. Here are some common operations.

Cloning a Repository

$repo = git_clone("https://github.com/nanodano/reference", "/tmp/reference");

Opening a repository

$repo = git_repository_open("/tmp/reference");

Git Blame

This will blame the README.md file from HEAD

$options = git_blame_options_new();
$blame = git_blame_file($repo, "README.md", $options);
$obj = git_revparse_single($repo, "HEAD:README.md");
$id = git_object_id($obj);
$blob = git_blob_lookup($repo, $id);
$raw = git_blob_rawcontent($blob);
$i = 0;
$lines = explode("\n", $raw);

foreach ($lines as $data) {
    $hunk = git_blame_get_hunk_byline($blame, $i+1);
    if (!$hunk) {
        continue;
    }
    $sig = sprintf("%s <%s>", $hunk['final_signature']['name'], $hunk['final_signature']['email']);
    printf("%s ( %-30s, %4d) %s\n", substr($hunk['final_commit_id'], 10),
        $sig,
        $i+1,
        $data
    );
    $i++;
}

Git Diff

$tree = git_tree_lookup($repo, "e14ccb8e18d632d78ce2f0aeb06597a03f42b237");
$diff = git_diff_tree_to_workdir($repo, $tree, git_diff_options_init());
$p = array();
git_diff_print($diff, GIT_DIFF_FORMAT_PATCH, function($diff_delta, $diff_hunk, $diff_line, $payload){
    if ($diff_line['origin'] == "-" || $diff_line['origin'] == "+") {
        echo $diff_line['origin'];
    }
    echo $diff_line['content'];
}, $p);

Git Patch

$tree = git_tree_lookup($repo, "e14ccb8e18d632d78ce2f0aeb06597a03f42b237");
$diff = git_diff_tree_to_workdir($repo, $tree, git_diff_options_init());
$payload = array();
$patch = git_patch_from_diff($diff, 1);
git_patch_print($patch, function($diff_delta, $diff_hunk, $diff_line, $payload){
    if ($diff_line['origin'] == "-" || $diff_line['origin'] == "+") {
        echo $diff_line['origin'];
    }
    echo $diff_line['content'];
}, $payload);

Git Push

$payload = array();
$repo    = git_repository_open(".");
$remote  = git_remote_load($repo, "origin");
$remote_callbacks = [
    "credentials" => function($url, $username_from_url, $allowed_types, &$payload) {
        // Build with LibSSH2 to use ssh protocol.
        if ($allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT) {
            return git_cred_userpass_plaintext_new("nanodano", getenv("GITHUB_TOKEN"));
        } else {
            error_log("not supported allowed types");
        }
}];

git_remote_set_callbacks($remote, $remote_callbacks);

if (git_remote_connect($remote, GIT_DIRECTION_PUSH)) {
    $push = git_push_new($remote);
    git_push_add_refspec($push, "refs/heads/master:refs/heads/master");
    git_push_finish($push);
    git_push_unpack_ok($push);
    git_push_status_foreach($push, function($ref, $name, &$payload){
        var_dump($ref, $name, $payload);
    }, $payload);
    git_push_update_tips($push);
    git_remote_disconnect($remote);
}

Reference Lookup

$ref = git_reference_dwim($repo, "HEAD");
$obj =  git_reference_peel($ref, GIT_OBJ_ANY);
echo git_object_id($obj);

Reference Log

$reflog = git_reflog_read($repo, "HEAD");

$count = git_reflog_entrycount($reflog);

for ($i = 0; $i < $count; $i++) {
    $entry = git_reflog_entry_byindex($reflog, $i);
    var_dump(git_reflog_entry_committer($entry));
}

Git Status

$list = git_status_list_new($repo, array());

$payload = array();

printf("# Changes to be committed:\n");
printf("#   (use "git reset HEAD <file>..." to unstage)\n");
printf("#\n");

$cnt = git_status_list_entrycount($list);

for ($i = 0; $i < $cnt; $i++) {
    $entry = git_status_byindex($list, $i);
    $flags = $entry['status'];
    $stat = getStat($flags);
    if (is_array($entry['head_to_index'])) {
        printf("# %15s %s\n", $stat, $entry['head_to_index']['new_file']['path']);
    }
}

printf("#\n");
printf("# Changes not staged for commit:\n");
printf("#   (use "git add <file>..." to update what will be committed)\n");
printf("#   (use "git checkout -- <file>..." to discard changes in working directory)\n");
printf("#\n");

for ($i = 0; $i < $cnt; $i++) {
    $entry = git_status_byindex($list, $i);
    $flags = $entry['status'];
    $stat = getStat($flags);
    if (is_array($entry['index_to_workdir'])) {
        printf("# %15s %s\n", $stat, $entry['index_to_workdir']['new_file']['path']);
    }
}

printf("#\n");

function getStat($flags)
{
    $stat = "";
    if ($flags & GIT_STATUS_IGNORED) {
        return;
    }
    if ($flags == GIT_STATUS_CURRENT) {
        return;
    }
    if ($flags & GIT_STATUS_INDEX_NEW){
        $stat = "new file:";
    }
    if ($flags & GIT_STATUS_WT_NEW) {
        $stat = "untracked:";
    }
    if ($flags & GIT_STATUS_INDEX_MODIFIED ||$flags & GIT_STATUS_WT_MODIFIED) {
        $stat = "modified:";
    }
    if ($flags & GIT_STATUS_INDEX_DELETED || $flags & GIT_STATUS_WT_DELETED) {
        $stat = "deleted:";
    }
    if ($flags & GIT_STATUS_INDEX_RENAMED || $flags & GIT_STATUS_WT_RENAMED) {
        $stat = "renamed:";
    }
    if ($flags & GIT_STATUS_INDEX_TYPECHANGE || $flags & GIT_STATUS_WT_TYPECHANGE) {
        $stat = "typechange:";
    }
    return $stat;
}

Advertisement

Advertisement