基于libgit2 C语言库的php-git扩展fix bug辛酸史

看到此文,是否觉得体内洪荒之力爆发,饥渴难耐想吐槽、情不自禁想捐赠
本文为原创文章,尊重辛勤劳动,可以免费摘要、推荐或聚合,亦可完整转载,但完整转载需要标明原出处,违者必究。

支付宝微  信

前言

这是一篇极其没有节(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使用教程(特别篇)几个基本概念说明

	/**
	 * 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;
	}

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

	/**
	 * 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;
	}

调用代码:

  		$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);	
 		}

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

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

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);
}

显而易见,该函数的返回值不对,应该是资源却返回布尔值。当然,上述问题不在这里。由于没有经验,直接用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辛酸史

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


这是一篇原创文章,如果您觉得有价值,可以通过捐赠来支持我的创作~
捐赠者会展示在博客的某个页面,钱将会用在有价值的地方,思考中...


分类: C/C++, PHP, 技术, 编程 | 标签: , , , , | 2个评论 | Permalink

2个评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注