3 require_once dirname(__FILE__
) . '/Git/Lazy.php';
4 require_once dirname(__FILE__
) . '/Git/Actor.php';
5 require_once dirname(__FILE__
) . '/Git/Commit.php';
6 require_once dirname(__FILE__
) . '/Git/Exception.php';
7 require_once dirname(__FILE__
) . '/Git/Repo.php';
8 require_once dirname(__FILE__
) . '/Git/Tree.php';
9 require_once dirname(__FILE__
) . '/Git/Blob.php';
12 * Wrapper for Git executable. Lowest level interface.
17 const S
= DIRECTORY_SEPARATOR
;
19 static protected $executeKwargs = array(
20 'istream', 'withKeepCwd', 'withExtendedOutput',
21 'withExceptions', 'withRawOutput'
25 * Git executable to invoke. You can replace this with a full path
26 * to your Git executable/wrapper script, but be sure to escape
27 * everything properly!
29 static public $git = 'git';
32 * Current working directory.
37 * @param $dir Current working directory.
39 public function __construct($dir) {
44 * Returns current working directory.
46 public function getDir() {return $this->dir
;}
48 // GitPython appears to have an implementation of getting
49 // attributes which also calls process. We're only going to support
50 // pure method calls... for now.
51 // public function __get($name)
54 * Executes a command on shell and returns output.
55 * @warning $istream is a STRING not a HANDLE, as it is in the Python
56 * implementation. We might want to change this some time.
57 * @param $command Command argument list to handle.
58 * @param $istream Stdin string passed to subprocess.
59 * @param $options Lookup array of options. These options are:
60 * 'withKeepCwd' => Whether to use current working directory from
61 * getcwd() or the Git directory in $this->dir
62 * 'withExtendedOutput' => Whether to return array(status, stdout, stderr)
63 * 'withExceptions' => Whether to raise an exception if Git returns
64 * a non-zero exit status
65 * 'withRawOutput' => Whether to avoid stripping off trailing whitespace
66 * @return String stdout output when withExtendedOutput is false, see above
69 public function execute($command, $istream = null, $options = array()) {
70 if (is_array($command)) $command = implode(' ', $command);
72 $options = array_merge(array(
73 'withKeepCwd' => false,
74 'withExtendedOutput' => false,
75 'withExceptions' => true,
76 'withRawOutput' => false,
78 if ($options['withKeepCwd'] ||
is_null($this->dir
)) {
83 if (strtoupper(substr(PHP_OS
, 0, 3)) == 'WIN') {
84 // Windows dependent code, stolen from PHPT
85 $com = new COM('WScript.Shell');
86 $com->CurrentDirectory
= $cwd;
87 $proc = $com->Exec($command);
88 if (!is_null($istream)) $proc->StdIn
->Write($istream);
89 $stdout = $proc->StdOut
->ReadAll();
90 $stderr = $proc->StdErr
->ReadAll();
91 $status = $proc->ExitCode
;
93 // untested! Also stolen from PHPT
94 // this seems needlessly complicated
95 $pipes_template = array(
96 0 => array('pipe', 'r'),
97 1 => array('pipe', 'w'),
98 2 => array('pipe', 'w'),
99 3 => array('pipe', 'w'), // pipe to write exit code to
102 $proc = proc_open($command, $pipes_template, $pipes);
104 fwrite($pipes[0], $istream);
107 $stdout = stream_get_contents($pipes[1]);
110 $stderr = stream_get_contents($pipes[2]);
113 $status = trim(fread($pipes[3], 5));
116 $close = proc_close($proc);
117 if (empty($code)) $status = $close;
119 // We want $stderr, $stdout and $status
120 if (!$options['withRawOutput']) {
121 // This feels buggy to me, especially with git cat-file
122 $stdout = rtrim($stdout);
123 $stderr = rtrim($stderr);
125 if ($options['withExceptions'] && $status !== 0) {
126 throw new Git_Exception_Command($command, $status, $stderr);
128 // Trace code omitted
129 if ($options['withExtendedOutput']) {
130 return array($status, $stdout, $stderr);
137 * Transforms an associative array of arguments to command line options.
138 * Arguments can be like 'r' or 'no-commit'.
139 * @note Original Python version kwargs used 'no_commit'
140 * form due to Python conventions. We decided to use a more direct
141 * approach because our associative array allow them.
143 public function transformArgs($kwargs) {
145 foreach ($kwargs as $k => $v) {
146 if (strlen($k) == 1) {
147 if ($v === true) $args[] = "-$k";
148 else $args[] = "-$k$v";
151 if ($v === true) $args[] = "--$k";
152 else $args[] = "--$k=$v";
159 * Runs a given Git command with the specified arguments and return
160 * the result as a string.
161 * @param $method Name of command, but camelCased instead of dash-ified.
162 * @param $args Array of arguments. There is actually only one argument,
163 * which is an array of associative and numerically indexed
165 * @return String output.
167 public function __call($method, $raw_args) {
168 // split out "kwargs" (to be converted to options) from regular
169 // "args" (which get inserted normally)
172 foreach ($raw_args as $raw) {
173 if (is_array($raw)) $kwargs = $raw; // only one assoc array allowed!
176 for ($i = 0; isset($kwargs[$i]); $i++
) {
177 $args[] = $kwargs[$i];
180 // Grab "special" arguments prior to transformation
181 $executeKwargs = array();
182 foreach ($kwargs as $k => $v) {
183 if (isset(self
::$executeKwargs[$k])) {
184 $executeKwargs[$k] = $v;
188 // $args and $kwargs are interesting
189 $opt_args = $this->transformArgs($kwargs);
190 // parse through $args again to determine which ones are actually kwargs
193 $args = array_merge($opt_args, $ext_args);
194 // Convert methodName to method-name (our equivalent of dashify).
195 // This is kind of inefficient.
197 for ($i = 0, $max = strlen($method); $i < $max; $i++
) {
199 $command .= ctype_upper($c) ?
'-' . strtolower($c) : $c;
201 $call = array_merge(array(self
::$git, $command), $args);
202 $result = $this->execute($call);