2 Star 0 Fork 0

Git工具集 / Git.php

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
Git.php 16.82 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
<?php
/*
* Git.php
*
* A PHP git library
*
* @package Git.php
* @version 0.1.4
* @author James Brumond
* @copyright Copyright 2013 James Brumond
* @repo http://github.com/kbjr/Git.php
*/
if (__FILE__ == $_SERVER['SCRIPT_FILENAME']) die('Bad load order');
// ------------------------------------------------------------------------
/**
* Git Interface Class
*
* This class enables the creating, reading, and manipulation
* of git repositories.
*
* @class Git
*/
class Git {
/**
* Git executable location
*
* @var string
*/
protected static $bin = '/usr/bin/git';
/**
* Sets git executable path
*
* @param string $path executable location
*/
public static function set_bin($path) {
self::$bin = $path;
}
/**
* Gets git executable path
*/
public static function get_bin() {
return self::$bin;
}
/**
* Sets up library for use in a default Windows environment
*/
public static function windows_mode() {
self::set_bin('git');
}
/**
* Create a new git repository
*
* Accepts a creation path, and, optionally, a source path
*
* @access public
* @param string repository path
* @param string directory to source
* @return GitRepo
*/
public static function &create($repo_path, $source = null) {
return GitRepo::create_new($repo_path, $source);
}
/**
* Open an existing git repository
*
* Accepts a repository path
*
* @access public
* @param string repository path
* @return GitRepo
*/
public static function open($repo_path) {
return new GitRepo($repo_path);
}
/**
* Clones a remote repo into a directory and then returns a GitRepo object
* for the newly created local repo
*
* Accepts a creation path and a remote to clone from
*
* @access public
* @param string repository path
* @param string remote source
* @param string reference path
* @return GitRepo
**/
public static function &clone_remote($repo_path, $remote, $reference = null) {
//Changed the below boolean from true to false, since this appears to be a bug when not using a reference repo. A more robust solution may be appropriate to make it work with AND without a reference.
return GitRepo::create_new($repo_path, $remote, false, $reference);
}
/**
* Checks if a variable is an instance of GitRepo
*
* Accepts a variable
*
* @access public
* @param mixed variable
* @return bool
*/
public static function is_repo($var) {
return (get_class($var) == 'GitRepo');
}
}
// ------------------------------------------------------------------------
/**
* Git Repository Interface Class
*
* This class enables the creating, reading, and manipulation
* of a git repository
*
* @class GitRepo
*/
class GitRepo {
protected $repo_path = null;
protected $bare = false;
protected $envopts = array();
/**
* Create a new git repository
*
* Accepts a creation path, and, optionally, a source path
*
* @access public
* @param string repository path
* @param string directory to source
* @param string reference path
* @return GitRepo
*/
public static function &create_new($repo_path, $source = null, $remote_source = false, $reference = null) {
if (is_dir($repo_path) && file_exists($repo_path."/.git")) {
throw new Exception('"'.$repo_path.'" is already a git repository');
} else {
$repo = new self($repo_path, true, false);
if (is_string($source)) {
if ($remote_source) {
if (isset($reference)) {
if (!is_dir($reference) || !is_dir($reference.'/.git')) {
throw new Exception('"'.$reference.'" is not a git repository. Cannot use as reference.');
} else if (strlen($reference)) {
$reference = realpath($reference);
$reference = "--reference $reference";
}
}
$repo->clone_remote($source, $reference);
} else {
$repo->clone_from($source);
}
} else {
$repo->run('init');
}
return $repo;
}
}
/**
* Constructor
*
* Accepts a repository path
*
* @access public
* @param string repository path
* @param bool create if not exists?
* @return void
*/
public function __construct($repo_path = null, $create_new = false, $_init = true) {
if (is_string($repo_path)) {
$this->set_repo_path($repo_path, $create_new, $_init);
}
}
/**
* Set the repository's path
*
* Accepts the repository path
*
* @access public
* @param string repository path
* @param bool create if not exists?
* @param bool initialize new Git repo if not exists?
* @return void
*/
public function set_repo_path($repo_path, $create_new = false, $_init = true) {
if (is_string($repo_path)) {
if ($new_path = realpath($repo_path)) {
$repo_path = $new_path;
if (is_dir($repo_path)) {
// Is this a work tree?
if (file_exists($repo_path."/.git")) {
$this->repo_path = $repo_path;
$this->bare = false;
// Is this a bare repo?
} else if (is_file($repo_path."/config")) {
$parse_ini = parse_ini_file($repo_path."/config");
if ($parse_ini['bare']) {
$this->repo_path = $repo_path;
$this->bare = true;
}
} else {
if ($create_new) {
$this->repo_path = $repo_path;
if ($_init) {
$this->run('init');
}
} else {
throw new Exception('"'.$repo_path.'" is not a git repository');
}
}
} else {
throw new Exception('"'.$repo_path.'" is not a directory');
}
} else {
if ($create_new) {
if ($parent = realpath(dirname($repo_path))) {
mkdir($repo_path);
$this->repo_path = $repo_path;
if ($_init) $this->run('init');
} else {
throw new Exception('cannot create repository in non-existent directory');
}
} else {
throw new Exception('"'.$repo_path.'" does not exist');
}
}
}
}
/**
* Get the path to the git repo directory (eg. the ".git" directory)
*
* @access public
* @return string
*/
public function git_directory_path() {
if ($this->bare) {
return $this->repo_path;
} else if (is_dir($this->repo_path."/.git")) {
return $this->repo_path."/.git";
} else if (is_file($this->repo_path."/.git")) {
$git_file = file_get_contents($this->repo_path."/.git");
if(mb_ereg("^gitdir: (.+)$", $git_file, $matches)){
if($matches[1]) {
$rel_git_path = $matches[1];
return $this->repo_path."/".$rel_git_path;
}
}
}
throw new Exception('could not find git dir for '.$this->repo_path.'.');
}
/**
* Tests if git is installed
*
* @access public
* @return bool
*/
public function test_git() {
$descriptorspec = array(
1 => array('pipe', 'w'),
2 => array('pipe', 'w'),
);
$pipes = array();
$resource = proc_open(Git::get_bin(), $descriptorspec, $pipes);
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
foreach ($pipes as $pipe) {
fclose($pipe);
}
$status = trim(proc_close($resource));
return ($status != 127);
}
/**
* Run a command in the git repository
*
* Accepts a shell command to run
*
* @access protected
* @param string command to run
* @return string
*/
protected function run_command($command) {
$descriptorspec = array(
1 => array('pipe', 'w'),
2 => array('pipe', 'w'),
);
$pipes = array();
/* Depending on the value of variables_order, $_ENV may be empty.
* In that case, we have to explicitly set the new variables with
* putenv, and call proc_open with env=null to inherit the reset
* of the system.
*
* This is kind of crappy because we cannot easily restore just those
* variables afterwards.
*
* If $_ENV is not empty, then we can just copy it and be done with it.
*/
if(count($_ENV) === 0) {
$env = NULL;
foreach($this->envopts as $k => $v) {
putenv(sprintf("%s=%s",$k,$v));
}
} else {
$env = array_merge($_ENV, $this->envopts);
}
$cwd = $this->repo_path;
$resource = proc_open($command, $descriptorspec, $pipes, $cwd, $env);
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
foreach ($pipes as $pipe) {
fclose($pipe);
}
$status = trim(proc_close($resource));
if ($status) throw new Exception($stderr . "\n" . $stdout); //Not all errors are printed to stderr, so include std out as well.
return $stdout;
}
/**
* Run a git command in the git repository
*
* Accepts a git command to run
*
* @access public
* @param string command to run
* @return string
*/
public function run($command) {
return $this->run_command(Git::get_bin()." ".$command);
}
/**
* Runs a 'git status' call
*
* Accept a convert to HTML bool
*
* @access public
* @param bool return string with <br />
* @return string
*/
public function status($html = false) {
$msg = $this->run("status");
if ($html == true) {
$msg = str_replace("\n", "<br />", $msg);
}
return $msg;
}
/**
* Runs a `git add` call
*
* Accepts a list of files to add
*
* @access public
* @param mixed files to add
* @return string
*/
public function add($files = "*") {
if (is_array($files)) {
$files = '"'.implode('" "', $files).'"';
}
return $this->run("add $files -v");
}
/**
* Runs a `git rm` call
*
* Accepts a list of files to remove
*
* @access public
* @param mixed files to remove
* @param Boolean use the --cached flag?
* @return string
*/
public function rm($files = "*", $cached = false) {
if (is_array($files)) {
$files = '"'.implode('" "', $files).'"';
}
return $this->run("rm ".($cached ? '--cached ' : '').$files);
}
/**
* Runs a `git commit` call
*
* Accepts a commit message string
*
* @access public
* @param string commit message
* @param boolean should all files be committed automatically (-a flag)
* @return string
*/
public function commit($message = "", $commit_all = true) {
$flags = $commit_all ? '-av' : '-v';
return $this->run("commit ".$flags." -m ".escapeshellarg($message));
}
/**
* Runs a `git clone` call to clone the current repository
* into a different directory
*
* Accepts a target directory
*
* @access public
* @param string target directory
* @return string
*/
public function clone_to($target) {
return $this->run("clone --local ".$this->repo_path." $target");
}
/**
* Runs a `git clone` call to clone a different repository
* into the current repository
*
* Accepts a source directory
*
* @access public
* @param string source directory
* @return string
*/
public function clone_from($source) {
return $this->run("clone --local $source ".$this->repo_path);
}
/**
* Runs a `git clone` call to clone a remote repository
* into the current repository
*
* Accepts a source url
*
* @access public
* @param string source url
* @param string reference path
* @return string
*/
public function clone_remote($source, $reference) {
return $this->run("clone $reference $source ".$this->repo_path);
}
/**
* Runs a `git clean` call
*
* Accepts a remove directories flag
*
* @access public
* @param bool delete directories?
* @param bool force clean?
* @return string
*/
public function clean($dirs = false, $force = false) {
return $this->run("clean".(($force) ? " -f" : "").(($dirs) ? " -d" : ""));
}
/**
* Runs a `git branch` call
*
* Accepts a name for the branch
*
* @access public
* @param string branch name
* @return string
*/
public function create_branch($branch) {
return $this->run("branch " . escapeshellarg($branch));
}
/**
* Runs a `git branch -[d|D]` call
*
* Accepts a name for the branch
*
* @access public
* @param string branch name
* @return string
*/
public function delete_branch($branch, $force = false) {
return $this->run("branch ".(($force) ? '-D' : '-d')." $branch");
}
/**
* Runs a `git branch` call
*
* @access public
* @param bool keep asterisk mark on active branch
* @return array
*/
public function list_branches($keep_asterisk = false) {
$branchArray = explode("\n", $this->run("branch"));
foreach($branchArray as $i => &$branch) {
$branch = trim($branch);
if (! $keep_asterisk) {
$branch = str_replace("* ", "", $branch);
}
if ($branch == "") {
unset($branchArray[$i]);
}
}
return $branchArray;
}
/**
* Lists remote branches (using `git branch -r`).
*
* Also strips out the HEAD reference (e.g. "origin/HEAD -> origin/master").
*
* @access public
* @return array
*/
public function list_remote_branches() {
$branchArray = explode("\n", $this->run("branch -r"));
foreach($branchArray as $i => &$branch) {
$branch = trim($branch);
if ($branch == "" || strpos($branch, 'HEAD -> ') !== false) {
unset($branchArray[$i]);
}
}
return $branchArray;
}
/**
* Returns name of active branch
*
* @access public
* @param bool keep asterisk mark on branch name
* @return string
*/
public function active_branch($keep_asterisk = false) {
$branchArray = $this->list_branches(true);
$active_branch = preg_grep("/^\*/", $branchArray);
reset($active_branch);
if ($keep_asterisk) {
return current($active_branch);
} else {
return str_replace("* ", "", current($active_branch));
}
}
/**
* Runs a `git checkout` call
*
* Accepts a name for the branch
*
* @access public
* @param string branch name
* @return string
*/
public function checkout($branch) {
return $this->run("checkout " . escapeshellarg($branch));
}
/**
* Runs a `git merge` call
*
* Accepts a name for the branch to be merged
*
* @access public
* @param string $branch
* @return string
*/
public function merge($branch) {
return $this->run("merge " . escapeshellarg($branch) . " --no-ff");
}
/**
* Runs a git fetch on the current branch
*
* @access public
* @return string
*/
public function fetch() {
return $this->run("fetch");
}
/**
* Add a new tag on the current position
*
* Accepts the name for the tag and the message
*
* @param string $tag
* @param string $message
* @return string
*/
public function add_tag($tag, $message = null) {
if (is_null($message)) {
$message = $tag;
}
return $this->run("tag -a $tag -m " . escapeshellarg($message));
}
/**
* List all the available repository tags.
*
* Optionally, accept a shell wildcard pattern and return only tags matching it.
*
* @access public
* @param string $pattern Shell wildcard pattern to match tags against.
* @return array Available repository tags.
*/
public function list_tags($pattern = null) {
$tagArray = explode("\n", $this->run("tag -l $pattern"));
foreach ($tagArray as $i => &$tag) {
$tag = trim($tag);
if (empty($tag)) {
unset($tagArray[$i]);
}
}
return $tagArray;
}
/**
* Push specific branch (or all branches) to a remote
*
* Accepts the name of the remote and local branch.
* If omitted, the command will be "git push", and therefore will take
* on the behavior of your "push.defualt" configuration setting.
*
* @param string $remote
* @param string $branch
* @return string
*/
public function push($remote = "", $branch = "") {
//--tags removed since this was preventing branches from being pushed (only tags were)
return $this->run("push $remote $branch");
}
/**
* Pull specific branch from remote
*
* Accepts the name of the remote and local branch.
* If omitted, the command will be "git pull", and therefore will take on the
* behavior as-configured in your clone / environment.
*
* @param string $remote
* @param string $branch
* @return string
*/
public function pull($remote = "", $branch = "") {
return $this->run("pull $remote $branch");
}
/**
* List log entries.
*
* @param strgin $format
* @return string
*/
public function log($format = null, $fulldiff=false, $filepath=null, $follow=false) {
$diff = "";
if ($fulldiff){
$diff = "--full-diff -p ";
}
if ($follow){
// Can't use full-diff with follow
$diff = "--follow -- ";
}
if ($format === null)
return $this->run('log ' . $diff . $filepath);
else
return $this->run('log --pretty=format:"' . $format . '" ' . $diff .$filepath);
}
/**
* Sets the project description.
*
* @param string $new
*/
public function set_description($new) {
$path = $this->git_directory_path();
file_put_contents($path."/description", $new);
}
/**
* Gets the project description.
*
* @return string
*/
public function get_description() {
$path = $this->git_directory_path();
return file_get_contents($path."/description");
}
/**
* Sets custom environment options for calling Git
*
* @param string key
* @param string value
*/
public function setenv($key, $value) {
$this->envopts[$key] = $value;
}
}
/* End of file */
PHP
1
https://gitee.com/vcs-all-in-one/Git.php.git
git@gitee.com:vcs-all-in-one/Git.php.git
vcs-all-in-one
Git.php
Git.php
master

搜索帮助