Teknik is a suite of services with attractive and functional interfaces. https://www.teknik.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CI_Git.php 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP 4.3.2 or newer
  6. *
  7. * @package CodeIgniter
  8. * @author ExpressionEngine Dev Team
  9. * @copyright Copyright (c) 2008 - 2009, EllisLab, Inc.
  10. * @license http://codeigniter.com/user_guide/license.html
  11. * @link http://codeigniter.com
  12. * @since Version 1.0
  13. * @filesource
  14. */
  15. // ------------------------------------------------------------------------
  16. /**
  17. * CodeIgniter Git Interface Class
  18. *
  19. * This class enables the creating, reading, and manipulation
  20. * of git repositories.
  21. *
  22. * @package CodeIgniter
  23. * @subpackage Git.php
  24. * @category Libraries
  25. * @author James Brumond
  26. * @link http://code.kbjrweb.com/project/gitphp
  27. */
  28. class Git {
  29. /**
  30. * Create a new git repository
  31. *
  32. * Accepts a creation path, and, optionally, a source path
  33. *
  34. * @access public
  35. * @param string repository path
  36. * @param string directory to source
  37. * @return GitRepo
  38. */
  39. public function &create($repo_path, $source = null) {
  40. return GitRepo::create_new($repo_path, $source);
  41. }
  42. /**
  43. * Open an existing git repository
  44. *
  45. * Accepts a repository path
  46. *
  47. * @access public
  48. * @param string repository path
  49. * @return GitRepo
  50. */
  51. public function open($repo_path) {
  52. return new GitRepo($repo_path);
  53. }
  54. /**
  55. * Checks if a variable is an instance of GitRepo
  56. *
  57. * Accepts a variable
  58. *
  59. * @access public
  60. * @param mixed variable
  61. * @return bool
  62. */
  63. public static function is_repo($var) {
  64. return (get_class($var) == 'GitRepo');
  65. }
  66. }
  67. // ------------------------------------------------------------------------
  68. /**
  69. * Git Repository Interface Class
  70. *
  71. * This class enables the creating, reading, and manipulation
  72. * of a git repository
  73. *
  74. * @package CodeIgniter
  75. * @subpackage CIGit
  76. * @category Libraries
  77. * @author James Brumond
  78. * @link http://code.kbjrweb.com/project/gitphp
  79. */
  80. class GitRepo {
  81. protected $repo_path = null;
  82. protected $git_path = '/usr/bin/git';
  83. /**
  84. * Create a new git repository
  85. *
  86. * Accepts a creation path, and, optionally, a source path
  87. *
  88. * @access public
  89. * @param string repository path
  90. * @param string directory to source
  91. * @return GitRepo
  92. */
  93. public static function &create_new($repo_path, $source = null) {
  94. if (is_dir($repo_path) && file_exists($repo_path."/.git") && is_dir($repo_path."/.git")) {
  95. throw new Exception('"$repo_path" is already a git repository');
  96. } else {
  97. $repo = new self($repo_path, true, false);
  98. if (is_string($source))
  99. $repo->clone_from($source);
  100. else $repo->run('init');
  101. return $repo;
  102. }
  103. }
  104. /**
  105. * Constructor
  106. *
  107. * Accepts a repository path
  108. *
  109. * @access public
  110. * @param string repository path
  111. * @param bool create if not exists?
  112. * @return void
  113. */
  114. public function __construct($repo_path = null, $create_new = false, $_init = true) {
  115. if (is_string($repo_path))
  116. $this->set_repo_path($repo_path, $create_new, $_init);
  117. }
  118. /**
  119. * Set the repository's path
  120. *
  121. * Accepts the repository path
  122. *
  123. * @access public
  124. * @param string repository path
  125. * @param bool create if not exists?
  126. * @return void
  127. */
  128. public function set_repo_path($repo_path, $create_new = false, $_init = true) {
  129. if (is_string($repo_path)) {
  130. if ($new_path = realpath($repo_path)) {
  131. $repo_path = $new_path;
  132. if (is_dir($repo_path)) {
  133. if (file_exists($repo_path."/.git") && is_dir($repo_path."/.git")) {
  134. $this->repo_path = $repo_path;
  135. } else {
  136. if ($create_new) {
  137. $this->repo_path = $repo_path;
  138. if ($_init) $this->run('init');
  139. } else {
  140. throw new Exception('"$repo_path" is not a git repository');
  141. }
  142. }
  143. } else {
  144. throw new Exception('"$repo_path" is not a directory');
  145. }
  146. } else {
  147. if ($create_new) {
  148. if ($parent = realpath(dirname($repo_path))) {
  149. mkdir($repo_path);
  150. $this->repo_path = $repo_path;
  151. if ($_init) $this->run('init');
  152. } else {
  153. throw new Exception('cannot create repository in non-existent directory');
  154. }
  155. } else {
  156. throw new Exception('"$repo_path" does not exist');
  157. }
  158. }
  159. }
  160. }
  161. /**
  162. * Tests if git is installed
  163. *
  164. * @access public
  165. * @return bool
  166. */
  167. public function test_git() {
  168. $descriptorspec = array(
  169. 1 => array('pipe', 'w'),
  170. 2 => array('pipe', 'w'),
  171. );
  172. $pipes = array();
  173. $resource = proc_open($this->git_path, $descriptorspec, $pipes);
  174. $stdout = stream_get_contents($pipes[1]);
  175. $stderr = stream_get_contents($pipes[2]);
  176. foreach ($pipes as $pipe) {
  177. fclose($pipe);
  178. }
  179. $status = trim(proc_close($resource));
  180. return ($status != 127);
  181. }
  182. /**
  183. * Run a command in the git repository
  184. *
  185. * Accepts a shell command to run
  186. *
  187. * @access protected
  188. * @param string command to run
  189. * @return string
  190. */
  191. protected function run_command($command) {
  192. $descriptorspec = array(
  193. 1 => array('pipe', 'w'),
  194. 2 => array('pipe', 'w'),
  195. );
  196. $pipes = array();
  197. $resource = proc_open($command, $descriptorspec, $pipes, $this->repo_path);
  198. $stdout = stream_get_contents($pipes[1]);
  199. $stderr = stream_get_contents($pipes[2]);
  200. foreach ($pipes as $pipe) {
  201. fclose($pipe);
  202. }
  203. $status = trim(proc_close($resource));
  204. if ($status) throw new Exception($stderr);
  205. return $stdout;
  206. }
  207. /**
  208. * Run a git command in the git repository
  209. *
  210. * Accepts a git command to run
  211. *
  212. * @access public
  213. * @param string command to run
  214. * @return string
  215. */
  216. public function run($command) {
  217. return $this->run_command($this->git_path." ".$command);
  218. }
  219. /**
  220. * Runs a `git add` call
  221. *
  222. * Accepts a list of files to add
  223. *
  224. * @access public
  225. * @param mixed files to add
  226. * @return string
  227. */
  228. public function add($files = "*") {
  229. if (is_array($files)) $files = '"'.implode('" "', $files).'"';
  230. return $this->run("add $files -v");
  231. }
  232. /**
  233. * Runs a `git commit` call
  234. *
  235. * Accepts a commit message string
  236. *
  237. * @access public
  238. * @param string commit message
  239. * @return string
  240. */
  241. public function commit($message = "") {
  242. return $this->run("commit -av -m \"$message\"");
  243. }
  244. /**
  245. * Runs a `git clone` call to clone the current repository
  246. * into a different directory
  247. *
  248. * Accepts a target directory
  249. *
  250. * @access public
  251. * @param string target directory
  252. * @return string
  253. */
  254. public function clone_to($target) {
  255. return $this->run("clone --local ".$this->repo_path." $target");
  256. }
  257. /**
  258. * Runs a `git clone` call to clone a different repository
  259. * into the current repository
  260. *
  261. * Accepts a source directory
  262. *
  263. * @access public
  264. * @param string source directory
  265. * @return string
  266. */
  267. public function clone_from($source) {
  268. return $this->run("clone --local $source ".$this->repo_path);
  269. }
  270. /**
  271. * Runs a `git clone` call to clone a remote repository
  272. * into the current repository
  273. *
  274. * Accepts a source url
  275. *
  276. * @access public
  277. * @param string source url
  278. * @return string
  279. */
  280. public function clone_remote($source) {
  281. return $this->run("clone $source ".$this->repo_path);
  282. }
  283. /**
  284. * Runs a `git clean` call
  285. *
  286. * Accepts a remove directories flag
  287. *
  288. * @access public
  289. * @param bool delete directories?
  290. * @return string
  291. */
  292. public function clean($dirs = false) {
  293. return $this->run("clean".(($dirs) ? " -d" : ""));
  294. }
  295. /**
  296. * Runs a `git branch` call
  297. *
  298. * Accepts a name for the branch
  299. *
  300. * @access public
  301. * @param string branch name
  302. * @return string
  303. */
  304. public function create_branch($branch) {
  305. return $this->run("branch $branch");
  306. }
  307. /**
  308. * Runs a `git branch -[d|D]` call
  309. *
  310. * Accepts a name for the branch
  311. *
  312. * @access public
  313. * @param string branch name
  314. * @return string
  315. */
  316. public function delete_branch($branch, $force = false) {
  317. return $this->run("branch ".(($force) ? '-D' : '-d')." $branch");
  318. }
  319. /**
  320. * Runs a `git branch` call
  321. *
  322. * @access public
  323. * @param bool keep asterisk mark on active branch
  324. * @return array
  325. */
  326. public function list_branches($keep_asterisk = false) {
  327. $branchArray = explode("\n", $this->run("branch"));
  328. foreach($branchArray as $i => &$branch) {
  329. $branch = trim($branch);
  330. if (! $keep_asterisk)
  331. $branch = str_replace("* ", "", $branch);
  332. if ($branch == "")
  333. unset($branchArray[$i]);
  334. }
  335. return $branchArray;
  336. }
  337. /**
  338. * Returns name of active branch
  339. *
  340. * @access public
  341. * @param bool keep asterisk mark on branch name
  342. * @return string
  343. */
  344. public function active_branch($keep_asterisk = false) {
  345. $branchArray = $this->list_branches(true);
  346. $active_branch = preg_grep("/^\*/", $branchArray);
  347. reset($active_branch);
  348. if ($keep_asterisk)
  349. return current($active_branch);
  350. else
  351. return str_replace("* ", "", current($active_branch));
  352. }
  353. /**
  354. * Runs a `git checkout` call
  355. *
  356. * Accepts a name for the branch
  357. *
  358. * @access public
  359. * @param string branch name
  360. * @return string
  361. */
  362. public function checkout($branch) {
  363. return $this->run("checkout $branch");
  364. }
  365. /**
  366. * Searches for valid repositories on the specified path
  367. *
  368. * @param array $paths Array of paths where repositories will be searched
  369. * @return array Found repositories, containing their name, path and description
  370. */
  371. public function getRepositories($paths)
  372. {
  373. $allRepositories = array();
  374. foreach ($paths as $path) {
  375. $repositories = $this->recurseDirectory($path);
  376. if (empty($repositories)) {
  377. throw new \RuntimeException('There are no GIT repositories in ' . $path);
  378. }
  379. $allRepositories = array_merge($allRepositories, $repositories);
  380. }
  381. $allRepositories = array_unique($allRepositories, SORT_REGULAR);
  382. asort($allRepositories);
  383. return $allRepositories;
  384. }
  385. public function recurseDirectory($path, $topLevel = true)
  386. {
  387. $dir = new \DirectoryIterator($path);
  388. $repositories = array();
  389. foreach ($dir as $file) {
  390. if ($file->isDot()) {
  391. continue;
  392. }
  393. if (strrpos($file->getFilename(), '.') === 0) {
  394. continue;
  395. }
  396. if (!$file->isReadable()) {
  397. continue;
  398. }
  399. if ($file->isDir()) {
  400. $isBare = file_exists($file->getPathname() . '/HEAD');
  401. $isRepository = file_exists($file->getPathname() . '/.git/HEAD');
  402. if ($isRepository || $isBare) {
  403. if ($isBare) {
  404. $description = $file->getPathname() . '/description';
  405. } else {
  406. $description = $file->getPathname() . '/.git/description';
  407. }
  408. if (file_exists($description)) {
  409. $description = file_get_contents($description);
  410. } else {
  411. $description = null;
  412. }
  413. if (!$topLevel) {
  414. $repoName = $file->getPathInfo()->getFilename() . '/' . $file->getFilename();
  415. } else {
  416. $repoName = $file->getFilename();
  417. }
  418. $repositories[$repoName] = array(
  419. 'name' => $repoName,
  420. 'path' => $file->getPathname(),
  421. 'description' => $description
  422. );
  423. continue;
  424. } else {
  425. $repositories = array_merge($repositories, $this->recurseDirectory($file->getPathname(), false));
  426. }
  427. }
  428. }
  429. return $repositories;
  430. }
  431. }
  432. /* End of file CI_Git.php */
  433. /* Location: ./application/libraries/CI_Git.php */