文章目录

前言

这是一篇极其没有节(nei)操(rong)的文章。除非你真的无聊,请不要阅读,否则后果自负~

正文

最近,在忙活微博话题组的日构建工具。工具主要的功能并不算复杂。。。写着写着,外面雨过天晴,居然还放起爆竹了,什么鬼。

构建工具的主要功能正如介绍中所述的那样,提取产品、测试等基本信息、提取版本库(git)信息、检查(编译)源文件、自动部署项目与发送邮件等。在提取git库信息时,相对于之前利用shell_exec PHP原生函数提取svn信息的方式,打算利用扩展来提取信息。一来更规范、更有效率(微乎其微),二来专业。缺点是相对而言部署环境麻烦,因为需要安装git扩展到当前php运行环境中来。

但是,万万没想到官方推荐的php-git扩展库开发版本已有3年没有维护了。索性用吧,又能怎样。。。

安装

还算顺利,由于公司开发机没有cmake,yum源也不可用,懒得配置,直接download一套源码。

cd /root/down/php-git
git submodule init
git submodule update
cd /root/down/php-git/libgit2/build
/root/down/cmake-3.6.1-Linux-x86_64/bin/cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=OFF -DBUILD_CLAR=OFF -DCMAKE_C_FLAGS=-fPIC ..
/root/down/cmake-3.6.1-Linux-x86_64/bin/cmake --build .
cd /root/down/php-git
phpize
./configure --enable-git2-debug --with-php-config=/your/php/config/path/php-config
make
make install

上述步骤自行领会,注意上述适用于64位Linux系统,32位的Linux请见上述提到的php-git库说明文档。

使用

我们提取的git信息基本也就是从上一次部署版本,到该次部署版本之间的log和diff信息。所以,第一件事情,就是拿到这段的commit hash值。关于下文中提到的commit hash和tree hash概念可以自行google或参见博客libgit2使用教程(特别篇)几个基本概念说明

[code lang=”php”]
/**
* Get commit hash array through revwalk.
*
* @param $commitHash string
* @return array
*/
public function revwalk($commitHash)
{
$walker = git_revwalk_new($this->repository);
$result = array();
git_revwalk_push_range($walker, "{$commitHash}..HEAD");
while ($id = git_revwalk_next($walker)) {
$result[] = $id;
}
return $result;
}
[/code]

在拿到commit hash之后,将通过git_diff_tree_to_tree方法拿到每个commit hash之间的diff信息。通过方法名可以知道,这个diff信息是通过两个tree拿到的。

[code lang=”php”]
/**
* Diff.
*
* @param $oldTreeHash string
* @param $newTreeHash string
* @format $format int such as:
* GIT_DIFF_FORMAT_PATCH = 1u full git diff
* GIT_DIFF_FORMAT_PATCH_HEADER = 2u just the file headers of patch
* GIT_DIFF_FORMAT_RAW = 3u like git diff –raw
* GIT_DIFF_FORMAT_NAME_ONLY = 4u like git diff –name-only
* GIT_DIFF_FORMAT_NAME_STATUS = 5u like git diff –name-status
* @return array
*/
public function diffTreeToTree($oldTreeHash, $newTreeHash, $format = GIT_DIFF_FORMAT_PATCH) {
$newTree = git_tree_lookup($this->repository, $newTreeHash);
$oldTree = git_tree_lookup($this->repository, $oldTreeHash);
$opts = git_diff_options_init();
$result = array();
$fcc = array();
$this->lastDiff = array();
$fci = function($diff_delta, $diff_hunk, $diff_line, $payload) {
$result = ”;
if ($diff_line[‘origin’] == "-" || $diff_line[‘origin’] == "+") {
$result = $diff_line[‘origin’];
}
$result .= trim($diff_line[‘content’], "\n");
$this->lastDiff[] = $result;
};
/*
$opts[‘flags’] = 0;
$opts[‘version’] = 1;
$opts[‘context_lines’] = 2;
$opts[‘interhunk_lines’] = 10;
$opts[‘old_prefix’] = ‘a’;
$opts[‘new_prefix’] = ‘b’;
$opts[‘pathspec’] = array();
$opts[‘max_size’] = -1;
$opts[‘notify_cb’] = function() {
echo "notiry_cb…\n";
};
$opts[‘notify_payload’] = null;
*/
$diff = git_diff_tree_to_tree($this->repository, $oldTree, $newTree, $opts);
git_diff_print($diff, $format, $fci, $fcc);
return $this->lastDiff;
}

/**
* Get commit info.
*
* @param $commitHash string
* @return array
*/
public function commitInfo($commitHash)
{
$result= array();
$commit = git_commit_lookup_prefix($this->repository, $commitHash, strlen($commitHash));
$result[‘author’] = git_commit_author($commit);
$result[‘message’] = git_commit_message($commit);
$result[‘tree_id’] = git_commit_tree_id($commit);
$result[‘parent_count’] = git_commit_parentcount($commit);
for ($i = 0; $i < $result[‘parent_count’]; $i++) {
$result[‘parent_id’][$i] = git_commit_parent_id($commit, $i);
}
$result[‘committer’] = git_commit_committer($commit);
$result[‘time’] = git_commit_time($commit);
$result[‘id’] = git_commit_id($commit);
return $result;
}
[/code]

调用代码:

[code lang=”php”]
$repository = Config::get("common.product.cmd_path");
$gitModel = new GitModel($repository);
$lastCommitHash = ‘942382fb26ca94f381f5c84597b4974b1acbf027’;
$walks = $gitModel->revwalk($lastCommitHash);
array_push($walks, $lastCommitHash);
$length = count($walks);
$commits = array();
$commitsMap = array();
$diffsMap = array();

foreach ($walks as $walk) {
$commits[] = $gitModel->commitInfo($walk);
}
for ($i = 0; $i < $length – 1; $i++) {
$commitsMap[$commits[$i][‘id’]] = $commits[$i];
$diffsMap[$commits[$i][‘id’]] = $gitModel->diffTreeToTree($commits[$i][‘tree_id’],
$commits[$i + 1][‘tree_id’],
GIT_DIFF_FORMAT_NAME_STATUS);
}
[/code]

然而,在运行到git_diff_tree_to_tree方法时,却来个:segmentation fault。这个错误一般是内存使用错误,这nnd我怎么晓得。只好扒开源码,硬着脑皮看吧(C我是小白)。

正所谓千里之行,始于足下。一直想着研究研究PHP扩展,机会终于来了,带着兴奋我打开了”messy”的代码。

[code lang=”c”]
PHP_FUNCTION(git_diff_tree_to_tree)
{
int result = 0;
git_diff *diff = NULL;
zval *repo = NULL;
php_git2_t *_repo = NULL;
zval *old_tree = NULL;
php_git2_t *_old_tree = NULL;
zval *new_tree = NULL;
php_git2_t *_new_tree = NULL;
zval *opts = NULL;
git_diff_options options = {0};

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
"rrra", &repo, &old_tree, &new_tree, &opts) == FAILURE) {
return;
}

ZEND_FETCH_RESOURCE(_repo, php_git2_t*, &repo, -1, PHP_GIT2_RESOURCE_NAME, git2_resource_handle);
ZEND_FETCH_RESOURCE(_old_tree, php_git2_t*, &old_tree, -1, PHP_GIT2_RESOURCE_NAME, git2_resource_handle);
ZEND_FETCH_RESOURCE(_new_tree, php_git2_t*, &new_tree, -1, PHP_GIT2_RESOURCE_NAME, git2_resource_handle);
php_git2_array_to_git_diff_options(&options, opts TSRMLS_CC);
result = git_diff_tree_to_tree(&diff, PHP_GIT2_V(_repo, repository), PHP_GIT2_V(_old_tree, tree), PHP_GIT2_V(_new_tree, tree), opts);
php_git2_git_diff_options_free(&options);
RETURN_LONG(result);
}
[/code]

显而易见,该函数的返回值不对,应该是资源却返回布尔值。当然,上述问题不在这里。由于没有经验,直接用php_printf打印信息跟踪调试。

在此期间,也曾给github上维护最活跃的技术牛人发过邮件和Twitter,可是未果。

libgit git_diff_tree_to_tree 邮件咨询
邮件咨询

 

libgit git_diff_tree_to_tree Twitter回复
Twitter回复

本人非常理解,通常来说我也没有太多时间和心思为其他人解决所谓的“难题”。对我来说,那些很没礼貌的询问我直接忽略。尽管我没得到答案,但是也算是尊总对方的。

最后发现,原来是PHP传过来的$opts参数不对。说是简单,就这一句话我整整用了一天时间,晚上10点钟才走。。

第二天中午,头脑清醒后看着C代码,如醍醐灌顶一般,尼玛opts参数传的居然是zval类型,在下图中的51、52行可知,需要的是git_diff_options类型。将上述代码改正如下(已提交pull request),编译安装顺利跑通(目前为止已经看到源码的些许问题,抽时间会继续fix bug)。

QQ截图20160827171047

这次的提交,代表着我慢慢在工作中步入C的世界。just go for it~


参考文章:

文章来源:

胡旭个人博客 => 基于libgit2 C语言库的php-git扩展fix bug辛酸史

转载请注明出处,违者必究!

Share:

2 comments

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.