The branch PHP_POST_RECEIVE on karma.git has been updated via 4d61fb520544f543d8c6120fb02548de4bfc2f06 (commit) via fc047465fda04319c5a14707d7709fff1f2a3f57 (commit) via b1240df188897323ee2c550d62fcab4a245bec37 (commit) via fc9c78fe12aa756060684e565adf8fb8a4373120 (commit) via c388fdc594d5cf8f8f695eef47c179e2e1acc39e (commit) from 8507e88c7abde403c9bdf1b385d5d4f2b284d1f3 (commit)
http://git.php.net/?p=karma.git;a=log;h=4d61fb520544f543d8c6120fb02548de4bfc2f06;hp=8507e88c7abde403c9bdf1b385d5d4f2b284d1f3 Summary of changes: hooks/pre-receive | 10 +++- lib/Git.php | 29 ++++++++++++-- lib/Git/PostReceiveHook.php | 88 +++++++++++++++++-------------------------- lib/Git/PreReceiveHook.php | 67 ++++++++++----------------------- lib/Git/PushInformation.php | 18 ++------- lib/Git/ReceiveHook.php | 55 ++++++++++++++++++++++++-- 6 files changed, 141 insertions(+), 126 deletions(-) -- Log ---------------------------------------- commit 4d61fb520544f543d8c6120fb02548de4bfc2f06 Author: Alexander Moskaliov <ir...@php.net> Date: Sun Mar 11 13:13:29 2012 +0400 next part of refactoring diff --git a/lib/Git.php b/lib/Git.php index ee3e95c..87e5fa9 100644 --- a/lib/Git.php +++ b/lib/Git.php @@ -5,6 +5,8 @@ class Git const GIT_EXECUTABLE = 'git'; const NULLREV = '0000000000000000000000000000000000000000'; + private static $repositoryPath = null; + /** * Returns the path to the current repository. * @@ -15,11 +17,30 @@ class Git */ public static function getRepositoryPath() { - $path = exec(sprintf('%s rev-parse --git-dir', self::GIT_EXECUTABLE)); - if (!is_dir($path)) { - return false; + if (is_null(self::$repositoryPath)) { + $path = exec(sprintf('%s rev-parse --git-dir', self::GIT_EXECUTABLE)); + self::$repositoryPath = is_dir($path) ? realpath($path) : false; } - return realpath($path); + return self::$repositoryPath; + } + + /** + * Run git shell command and return result + * + * @param $cmd string + * @param $arg string + * @param ... string + * @return string + */ + public static function gitExec($cmd) + { + $cmd = self::GIT_EXECUTABLE . " --git-dir=" . self::getRepositoryPath() . " " . $cmd; + $args = func_get_args(); + array_shift($args); + $cmd = vsprintf($cmd, $args); + $output = shell_exec($cmd); + if ($output === NULL) throw new \Exception('Failed to call git'); + return $output; } } diff --git a/lib/Git/PostReceiveHook.php b/lib/Git/PostReceiveHook.php index 0f36aa0..1d5daee 100644 --- a/lib/Git/PostReceiveHook.php +++ b/lib/Git/PostReceiveHook.php @@ -92,20 +92,22 @@ class PostReceiveHook extends ReceiveHook if ($changeType == self::TYPE_UPDATED) { // check if push was with --force option - if ($replacedRevisions = $this->getRevisions($newrev . '..' . $oldrev)) { + if ($replacedRevisions = $this->getRevisions(escapeshellarg($newrev . '..' . $oldrev))) { $message .= "Discarded revisions: \n" . implode("\n", $replacedRevisions) . "\n"; } // git rev-list old..new - $revisions = $this->getRevisions($oldrev . '..' . $newrev); + $revisions = $this->getRevisions(escapeshellarg($oldrev . '..' . $newrev)); } else { // for new branch we write log about new commits only - $revisions = $this->getRevisions($newrev. ' --not ' . implode(' ', array_diff($this->allBranches, $this->newBranches))); + $revisions = $this->getRevisions( + escapeshellarg($newrev) . ' --not ' . implode(' ', $this->escapeArrayShellArgs(array_diff($this->allBranches, $this->newBranches))) + ); foreach ($this->updatedBranches as $refname) { if ($this->isRevExistsInBranches($this->refs[$refname]['old'], [$name])) { - $this->cacheRevisions($name, $this->getRevisions($this->refs[$refname]['old'] . '..' . $newrev)); + $this->cacheRevisions($name, $this->getRevisions(escapeshellarg($this->refs[$refname]['old'] . '..' . $newrev))); } } } @@ -115,9 +117,9 @@ class PostReceiveHook extends ReceiveHook if (count($revisions)) { $message .= "--------LOG--------\n"; foreach ($revisions as $revision) { - $diff = $this->gitExecute( + $diff = \Git::gitExec( 'diff-tree --stat --pretty=medium -c %s', - $revision + escapeshellarg($revision) ); $message .= $diff."\n\n"; @@ -185,7 +187,7 @@ class PostReceiveHook extends ReceiveHook private function getTagInfo($tag) { $info = "Target:\n"; - $info .= $this->gitExecute('diff-tree --stat --pretty=medium -c %s', $tag); + $info .= \Git::gitExec('diff-tree --stat --pretty=medium -c %s', escapeshellarg($tag)); return $info; } @@ -195,14 +197,14 @@ class PostReceiveHook extends ReceiveHook */ private function getAnnotatedTagInfo($tag) { - $tagInfo = $this->gitExecute('for-each-ref --format="%%(*objectname) %%(taggername) %%(taggerdate)" %s', $tag); - list($target, $tagger, $taggerdate) = explode(' ', $tagInfo); + $tagInfo = \Git::gitExec('for-each-ref --format="%%(*objectname) %%(taggername) %%(taggerdate)" %s', escapeshellarg($tag)); + list($target, $tagger, $taggerDate) = explode(' ', $tagInfo); $info = "Tagger: " . $tagger . "\n"; - $info .= "Date: " . $taggerdate . "\n"; - $info .= $this->gitExecute("cat-file tag %s | sed -e '1,/^$/d'", $tag)."\n"; + $info .= "Date: " . $taggerDate . "\n"; + $info .= \Git::gitExec("cat-file tag %s | sed -e '1,/^$/d'", escapeshellarg($tag))."\n"; $info .= "Target:\n"; - $info .= $this->gitExecute('diff-tree --stat --pretty=medium -c %s', $target); + $info .= \Git::gitExec('diff-tree --stat --pretty=medium -c %s', escapeshellarg($target)); return $info; } @@ -212,16 +214,20 @@ class PostReceiveHook extends ReceiveHook */ private function isAnnotatedTag($rev) { - return trim($this->gitExecute('for-each-ref --format="%%(objecttype)" %s', $rev)) == 'tag'; + return trim(\Git::gitExec('for-each-ref --format="%%(objecttype)" %s', escapeshellarg($rev))) == 'tag'; } /** - * @param $revRange string + * Get list of revisions for $revRange + * + * Required already escaped string in $revRange!!! + * + * @param $revRange string A..B or A ^B C --not D etc. * @return array */ private function getRevisions($revRange) { - $output = $this->gitExecute( + $output = \Git::gitExec( 'rev-list %s', $revRange ); @@ -233,7 +239,7 @@ class PostReceiveHook extends ReceiveHook private function getCommitInfo($revision) { - $raw = $this->gitExecute('rev-list -n 1 --format="%%P%%n%%an%%n%%ae%%n%%aD%%n%%cn%%n%%ce%%n%%cD%%n%%B" %s', $revision); + $raw = \Git::gitExec('rev-list -n 1 --format="%%P%%n%%an%%n%%ae%%n%%aD%%n%%cn%%n%%ce%%n%%cD%%n%%B" %s', escapeshellarg($revision)); $raw = explode("\n", $raw, 9); //8 elements separated by \n, last element - log message, first(skipped) element - "commit sha" return [ 'parents' => $raw[1], // %P @@ -279,13 +285,13 @@ class PostReceiveHook extends ReceiveHook { $info = $this->getCommitInfo($revision); - $paths = $this->getChangedPaths($revision); + $paths = $this->getChangedPaths(escapeshellarg($revision)); $pathsString = ''; foreach ($paths as $path => $action) { $pathsString .= ' ' . $action . ' ' . $path . "\n"; } - $diff = $this->gitExecute('diff-tree -c -p %s', $revision); + $diff = \Git::gitExec('diff-tree -c -p %s', escapeshellarg($revision)); $mail = new \Mail(); $mail->setSubject($this->emailPrefix . '[commit] ' . $this->getRepositoryName() . ' ' . implode(' ', array_keys($paths))); @@ -364,7 +370,7 @@ class PostReceiveHook extends ReceiveHook * @return bool */ private function isRevExistsInBranches($revision, array $branches) { - return !(bool) $this->gitExecute('rev-list --max-count=1 %s --not %s', $revision, implode(' ', $branches)); + return !(bool) \Git::gitExec('rev-list --max-count=1 %s --not %s', escapeshellarg($revision), implode(' ', $this->escapeArrayShellArgs($branches))); } } diff --git a/lib/Git/PreReceiveHook.php b/lib/Git/PreReceiveHook.php index 20672a9..2e93012 100644 --- a/lib/Git/PreReceiveHook.php +++ b/lib/Git/PreReceiveHook.php @@ -19,7 +19,7 @@ class PreReceiveHook extends ReceiveHook */ public function isKarmaIgnored() { - return 'true' === exec(sprintf('%s config karma.ignored', \Git::GIT_EXECUTABLE)); + return 'true' === \Git::gitExec('%s config karma.ignored'); } public function mapInput(callable $fn) { @@ -68,7 +68,7 @@ class PreReceiveHook extends ReceiveHook $parsed_input); /* flattern the array */ - $flattend = array_reduce($paths, 'array_merge', []); + $paths = array_reduce($paths, 'array_merge', []); return array_unique($paths); diff --git a/lib/Git/PushInformation.php b/lib/Git/PushInformation.php index edf73dc..4be1c0f 100644 --- a/lib/Git/PushInformation.php +++ b/lib/Git/PushInformation.php @@ -3,17 +3,11 @@ namespace Git; class PushInformation { - const GIT_EXECUTABLE = 'git'; - - private $karmaFile; - private $repositoryBasePath; private $hook = null; - private $repourl = null; public function __construct(ReceiveHook $hook) { - $this->repourl = \Git::getRepositoryPath(); $this->hook = $hook; } @@ -27,17 +21,10 @@ class PushInformation */ protected function mergeBase($oldrev, $newrev) { - $baserev = exec(sprintf('%s --git-dir=%s merge-base %s %s', - \Git::GIT_EXECUTABLE, - $this->repourl, - escapeshellarg($oldrev), - escapeshellarg($newrev)), $output, $retval); + $baserev = \Git::gitExec('merge-base %s %s', escapeshellarg($oldrev), escapeshellarg($newrev)); $baserev = trim($baserev); - if (0 !== $retval) { - throw new \Exception('Failed to call git'); - } if (40 != strlen($baserev)) { return false; diff --git a/lib/Git/ReceiveHook.php b/lib/Git/ReceiveHook.php index 58fe0c3..650a855 100644 --- a/lib/Git/ReceiveHook.php +++ b/lib/Git/ReceiveHook.php @@ -13,34 +13,16 @@ abstract class ReceiveHook const REF_TAG = 1; private $repositoryName = ''; - protected $repositoryPath = ''; public function __construct($basePath) { - $this->repositoryPath = \Git::getRepositoryPath(); - $rel_path = str_replace($basePath, '', $this->repositoryPath); + $rel_path = str_replace($basePath, '', \Git::getRepositoryPath()); if (preg_match('@/(.*\.git)$@', $rel_path, $matches)) { $this->repositoryName = $matches[1]; } } /** - * Run git shell command and return result - * - * @param $cmd string - * @return string - */ - protected function gitExecute($cmd) - { - $cmd = \Git::GIT_EXECUTABLE . " --git-dir=" . $this->repositoryPath . " " . $cmd; - $args = func_get_args(); - array_shift($args); - $cmd = vsprintf($cmd, $args); - $output = shell_exec($cmd); - return $output; - } - - /** * Escape array items by escapeshellarg function * @param $args * @return array array with escaped items @@ -68,12 +50,15 @@ abstract class ReceiveHook * Return array with changed paths as keys and change type as values * If commit is merge commit change type will have more than one char * (for example "MM") + * + * Required already escaped string in $revRange!!! + * * @param $revRange * @return array */ protected function getChangedPaths($revRange) { - $raw = $this->gitExecute('show --name-status --pretty="format:" %s', $revRange); + $raw = \Git::gitExec('show --name-status --pretty="format:" %s', $revRange); $paths = []; if (preg_match_all('/([ACDMRTUXB*]+)\s+([^\n\s]+)/', $raw , $matches, PREG_SET_ORDER)) { foreach($matches as $item) { @@ -91,7 +76,7 @@ abstract class ReceiveHook */ protected function getAllBranches() { - $branches = explode("\n", $this->gitExecute('for-each-ref --format="%%(refname)" "refs/heads/*"')); + $branches = explode("\n", \Git::gitExec('for-each-ref --format="%%(refname)" "refs/heads/*"')); if ($branches[0] == '') $branches = []; return $branches; } commit fc047465fda04319c5a14707d7709fff1f2a3f57 Merge: b1240df fc9c78f Author: Alexander Moskaliov <ir...@php.net> Date: Sun Mar 11 12:02:33 2012 +0400 Merge branch 'master' of git.php.net:/karma into PHP_POST_RECEIVE * 'master' of git.php.net:/karma: No comma needed Require access to all the repository in case we do a forced push Conflicts: hooks/post-receive hooks/pre-receive lib/Git/PushInformation.php lib/Git/ReceiveHook.php diff --cc hooks/pre-receive index 3370dc4,6a7c29f..c0e838d --- a/hooks/pre-receive +++ b/hooks/pre-receive @@@ -16,8 -18,9 +18,10 @@@ set_include_path PATH_SEPARATOR . get_include_path()); + include 'Git.php'; + include 'Git/PushInformation.php'; include 'Git/ReceiveHook.php'; +include 'Git/PreReceiveHook.php'; function deny($reason) { commit b1240df188897323ee2c550d62fcab4a245bec37 Author: Alexander Moskaliov <ir...@php.net> Date: Sun Mar 11 11:40:47 2012 +0400 second part of refactoring diff --git a/lib/Git/PostReceiveHook.php b/lib/Git/PostReceiveHook.php index 8250559..0f36aa0 100644 --- a/lib/Git/PostReceiveHook.php +++ b/lib/Git/PostReceiveHook.php @@ -34,28 +34,6 @@ class PostReceiveHook extends ReceiveHook } /** - * @param $cmd string - * @return string - */ - private function gitExecute($cmd) - { - $cmd = \Git::GIT_EXECUTABLE . " --git-dir=" . $this->repositoryPath . " " . $cmd; - $args = func_get_args(); - array_shift($args); - $cmd = vsprintf($cmd, $args); - $output = shell_exec($cmd); - return $output; - } - - /** - * @return array - */ - private function getAllBranches() - { - return explode("\n", $this->gitExecute('for-each-ref --format="%%(refname)" "refs/heads/*"')); - } - - /** * */ public function process() @@ -269,24 +247,14 @@ class PostReceiveHook extends ReceiveHook ]; } - private function getCommitChangedPaths($revision) - { - $raw = $this->gitExecute('show --name-status --pretty="format:" %s', $revision); - $paths = []; - if (preg_match_all('/([ACDMRTUXB*]+)\s+([^\n\s]+)/', $raw , $matches, PREG_SET_ORDER)) { - foreach($matches as $item) { - $paths[$item[2]] = $item[1]; - } - } - return $paths; - } - /** * Send mail about commit. * Subject: [git] [commit] %PROJECT% %PATHS% * Body: + * Commit: %SHA% * Author: %USER% Thu, 08 Mar 2012 12:39:48 +0000 * Committer: %USER% Thu, 08 Mar 2012 12:39:48 +0000 + * Parents: %SHA_PARENTS% * * Commit: http://git.php.net/?p=%PROJECT_PATH%;a=commitdiff;h=%SHA% * @@ -311,7 +279,7 @@ class PostReceiveHook extends ReceiveHook { $info = $this->getCommitInfo($revision); - $paths = $this->getCommitChangedPaths($revision); + $paths = $this->getChangedPaths($revision); $pathsString = ''; foreach ($paths as $path => $action) { @@ -335,27 +303,35 @@ class PostReceiveHook extends ReceiveHook if (strlen($pathsString) < 8192) { + // inline changed paths $message .= "Changed paths:\n" . $pathsString . "\n"; if ((strlen($pathsString) + strlen($diff)) < 8192) { + // inline diff $message .= "Diff:\n" . $diff . "\n"; } else { + // diff attach $diffFile = 'diff_' . $revision . '.txt'; $mail->addTextFile($diffFile, $diff); if ((strlen($message) + $mail->getFileLength($diffFile)) > 262144) { + // diff attach exceeded max size $mail->dropFile($diffFile); $message .= 'Diff: <Diff exceeded maximum size>'; } } } else { + // changed paths attach $pathsFile = 'paths_' . $revision . '.txt'; $mail->addTextFile($pathsFile, $pathsString); if ((strlen($message) + $mail->getFileLength($pathsFile)) > 262144) { + // changed paths attach exceeded max size $mail->dropFile($pathsFile); $message .= 'Changed paths: <changed paths exceeded maximum size>'; } else { + // diff attach $diffFile = 'diff_' . $revision . '.txt'; $mail->addTextFile($diffFile, $diff); if ((strlen($message) + $mail->getFileLength($pathsFile) + $mail->getFileLength($diffFile)) > 262144) { + // diff attach exceeded max size $mail->dropFile($diffFile); } } diff --git a/lib/Git/PreReceiveHook.php b/lib/Git/PreReceiveHook.php index c1e8e03..20672a9 100644 --- a/lib/Git/PreReceiveHook.php +++ b/lib/Git/PreReceiveHook.php @@ -3,7 +3,6 @@ namespace Git; class PreReceiveHook extends ReceiveHook { - const INPUT_PATTERN = '@^([0-9a-f]{40}) ([0-9a-f]{40}) (.+)$@i'; private $karmaFile; @@ -44,59 +43,33 @@ class PreReceiveHook extends ReceiveHook return file($this->karmaFile); } - /** - * Returns an array of files that were updated between revision $old and $new. - * - * @param string $old The old revison number. - * @parma string $new The new revison umber. - * - * @return array - */ - private function getReceivedPathsForRange($old, $new) - { - $repourl = \Git::getRepositoryPath(); - $output = []; - - /* there is the case where we push a new branch. check only new commits. - in case its a brand new repo, no heads will be available. */ - if ($old == \Git::NULLREV) { - exec( - sprintf("%s --git-dir=%s for-each-ref --format='%%(refname)' 'refs/heads/*'", - \Git::GIT_EXECUTABLE, $repourl), $output); - /* do we have heads? otherwise it's a new repo! */ - $heads = implode(' ', $output); - if (count($output) > 0) { - $not = array_map( - function($x) { - return sprintf('--not %s', escapeshellarg($x)); - }, $heads); - } - exec( - sprintf('%s --git-dir=%s log --name-only --pretty=format:"" %s %s', - \Git::GIT_EXECUTABLE, $repourl, $not, - escapeshellarg($new)), $output); - } else { - exec( - sprintf('%s --git-dir=%s log --name-only --pretty=format:"" %s..%s', - \Git::GIT_EXECUTABLE, $repourl, escapeshellarg($old), - escapeshellarg($new)), $output); - } - return $output; - } - public function getReceivedPaths() { $parsed_input = $this->hookInput(); + // escaped branches + $allBranches =$this->escapeArrayShellArgs($this->getAllBranches()); + $paths = array_map( - function ($input) { - return $this->getReceivedPathsForRange($input['old'], $input['new']); + function ($input) use ($allBranches) { + $paths = []; + + if ($input['changetype'] == self::TYPE_CREATED) { + $paths = $this->getChangedPaths(escapeshellarg($input['new']) . ' --not ' . implode(' ', $allBranches)); + } elseif ($input['changetype'] == self::TYPE_UPDATED) { + $paths = $this->getChangedPaths(escapeshellarg($input['old'] . '..' . $input['new'])); + } else { + // deleted branch. we also need some paths + // to check karma + } + + return array_keys($paths); }, $parsed_input); - /* remove empty lines, and flattern the array */ + /* flattern the array */ $flattend = array_reduce($paths, 'array_merge', []); - $paths = array_filter($flattend); + return array_unique($paths); } diff --git a/lib/Git/ReceiveHook.php b/lib/Git/ReceiveHook.php index 420cfe9..58fe0c3 100644 --- a/lib/Git/ReceiveHook.php +++ b/lib/Git/ReceiveHook.php @@ -25,6 +25,33 @@ abstract class ReceiveHook } /** + * Run git shell command and return result + * + * @param $cmd string + * @return string + */ + protected function gitExecute($cmd) + { + $cmd = \Git::GIT_EXECUTABLE . " --git-dir=" . $this->repositoryPath . " " . $cmd; + $args = func_get_args(); + array_shift($args); + $cmd = vsprintf($cmd, $args); + $output = shell_exec($cmd); + return $output; + } + + /** + * Escape array items by escapeshellarg function + * @param $args + * @return array array with escaped items + */ + protected function escapeArrayShellArgs($args) + { + return array_map('escapeshellarg', $args); + } + + + /** * Returns the repository name. * * A repository name is the path to the repository without the .git. @@ -38,14 +65,47 @@ abstract class ReceiveHook } /** + * Return array with changed paths as keys and change type as values + * If commit is merge commit change type will have more than one char + * (for example "MM") + * @param $revRange + * @return array + */ + protected function getChangedPaths($revRange) + { + $raw = $this->gitExecute('show --name-status --pretty="format:" %s', $revRange); + $paths = []; + if (preg_match_all('/([ACDMRTUXB*]+)\s+([^\n\s]+)/', $raw , $matches, PREG_SET_ORDER)) { + foreach($matches as $item) { + $paths[$item[2]] = $item[1]; + } + } + return $paths; + } + + + /** + * Return array with branches names in repository + * + * @return array + */ + protected function getAllBranches() + { + $branches = explode("\n", $this->gitExecute('for-each-ref --format="%%(refname)" "refs/heads/*"')); + if ($branches[0] == '') $branches = []; + return $branches; + } + + + /** * Parses the input from git. * * Git pipes a list of oldrev, newrev and revname combinations * to the hook. We parse this input. For more information about * the input see githooks(5). * - * Returns an array with 'old', 'new', 'refname' keys for each ref that - * will be updated. + * Returns an array with 'old', 'new', 'refname', 'changetype', 'reftype' + * keys for each ref that will be updated. * @return array */ public function hookInput() Thank you for your contribution. -- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php