import
[web.mtrack] / bin / git-commit-hook
1 #!/usr/bin/env php
2 <?php # vim:ts=2:sw=2:et:ft=php:
3 /* For licensing and copyright terms, see the file named LICENSE */
4 // called as:
5 // git-commit-hook what [mtrackconfig]
6 // the cwd is the repo path
7 // a list of "oldrev newrev refname" lines is presented to us on stdin
8
9 $action = $argv[1];
10 if (isset($argv[2])) {
11   putenv("MTRACK_CONFIG_FILE=" . $argv[2]);
12 }
13 include dirname(__FILE__) . '/../inc/common.php';
14 if (file_exists(MTrackConfig::get('core', 'vardir') . '/.initializing')) {
15   exit(0);
16 }
17
18 ini_set('display_errors', true);
19 $GIT = MTrackConfig::get('tools', 'git');
20
21 class GitCommitHookBridge implements IMTrackCommitHookBridge {
22   var $repo;
23   var $files = array();
24   var $log = array();
25   var $commits = array();
26
27   function __construct($repo) {
28     global $GIT;
29
30     $this->repo = $repo;
31
32     while (($line = fgets(STDIN)) !== false) {
33       list($old, $new, $ref) = explode(' ', trim($line), 3);
34       $this->commits[] = $new;
35
36       $fp = run($GIT, 'log', '--no-color', '--name-status',
37           '--date=rfc', $ref, "$old..$new");
38       $props = array();
39       $line = fgets($fp);
40       if (!preg_match("/^commit\s+(\S+)$/", $line)) {
41         throw new Exception("unexpected output from git log: $line");
42       }
43       while (($line = fgets($fp)) !== false) {
44         $line = rtrim($line);
45         if (!strlen($line)) break;
46         if (preg_match("/^(\S+):\s*(.*)\s*$/", $line, $M)) {
47           $props[$M[1]] = $M[2];
48         }
49       }
50       while (($line = fgets($fp)) !== false) {
51         $line = rtrim($line);
52         if (strncmp($line, '    ', 4)) {
53           break;
54         }
55         $this->log[] = substr($line, 4);
56       }
57       do {
58         if (preg_match("/^(.+)\s+(\S+)\s*$/", $line, $M)) {
59           $st = $M[1];
60           $file = $M[2];
61           $this->files[$file] = $new;
62         }
63       } while (($line = fgets($fp)) !== false);
64     }
65   }
66   function enumChangedOrModifiedFileNames() {
67     return array_keys($this->files);
68   }
69
70   function getCommitMessage() {
71     $log = join("\n", $this->log);
72     $log = preg_replace('/\[([a-fA-F0-9]+)\]/',
73       "[changeset:" . $this->repo->getBrowseRootName() . ",\$1]", $log);
74     return $log;
75   }
76
77   function getFileStream($path) {
78     global $GIT;
79     $rev = $this->files[$path];
80
81     // There may be a better way...
82     // ls-tree to determine the hash of the file from this change:
83     $fp = run($GIT, 'ls-tree', '-r', $rev, $path);
84     $line = fgets($fp);
85     $fp = null;
86     list($mode, $type, $hash, $name) = preg_split("/\s+/", $line);
87     // now we can cat that blob
88     return run($GIT, 'cat-file', 'blob', $hash);
89   }
90
91   function getChangesetDescriptor() {
92     $cs = array();
93     foreach ($this->commits as $ref) {
94       $cs[] = '[changeset:' . $this->repo->getBrowseRootName() . ",$ref]";
95     }
96     return join(", ", $cs);
97   }
98 }
99
100 try {
101   $repo = MTrackRepo::loadByLocation(getcwd());
102   $bridge = new GitCommitHookBridge($repo);
103   $author = MTrackAuth::whoami();
104   if ($author == 'anonymous') {
105     throw new Exception("cannot determine who you are");
106   }
107   $author = mtrack_canon_username($author);
108   MTrackAuth::su($author);
109   $checker = new MTrackCommitChecker($repo);
110   switch ($action) {
111     case 'pre':
112       $checker->preCommit($bridge);
113       break;
114     default:
115       $checker->postCommit($bridge);
116   }
117   exit(0);
118 } catch (Exception $e) {
119   fwrite(STDERR, "\n" . $e->getMessage() .
120     "\n\n" .
121     $e->getTraceAsString() .
122     "\n\n ** Commit failed [$action]\n");
123
124   exit(1);
125 }
126
127 function run()
128 {
129   $args = func_get_args();
130   $all_args = array();
131   foreach ($args as $a) {
132     if (is_array($a)) {
133       foreach ($a as $arg) {
134          $all_args[] = $arg;
135       }
136     } else {
137       $all_args[] = $a;
138     }
139   }
140
141   $cmd = '';
142
143   foreach ($all_args as $i => $arg) {
144     if ($i > 0) {
145       $cmd .= ' ';
146     }
147     $cmd .= escapeshellarg($arg);
148   }
149
150 //  echo $cmd, "\n";
151   return popen($cmd, 'r');
152 }