final move of files
[web.mtrack] / MTrack / SCM / Git / CommitHookBridge.php
1 <?php  
2 require_once 'Interface/CommitHookBridge.php';
3 require_once 'Config.php';
4 require_once 'Repo.php';
5
6 // needs to be run from git-recieve (and it has to be the only thing run.
7
8 class MTrack_SCM_Git_CommitHookBridge extends  IMTrackCommitHookBridge 
9 {
10     static public $GIT;
11     var $repo;
12     var $files = array();
13     var $log = array();
14     var $commits = array();
15     var $fileActions = array(); // file=> delete / modify etc..
16     /**
17     * fills up repo, files, log, commits by running log on the STDIN
18     */
19     function __construct(MTrackRepo $repo) 
20     {
21         self::$GIT = MTrackConfig::get('tools', 'git');
22         $this->repo = $repo;
23         while (($line = fgets(STDIN)) !== false) {
24           
25           list($old, $new, $ref) = explode(' ', trim($line), 3);
26           $this->commits[] = $new;
27
28           $fp = $this->run(self::$GIT, 'log', '--no-color', '--name-status',
29               '--date=rfc', $ref, "$old..$new");
30               
31               
32           $props = array();
33           $line = fgets($fp);
34           if (!preg_match("/^commit\s+(\S+)$/", $line)) {
35             throw new Exception("unexpected output from git log: $line");
36           }
37           // read key: value properties like Author: / Date: 
38           while (($line = fgets($fp)) !== false) {
39             $line = rtrim($line);
40             if (!strlen($line)) break;
41             if (preg_match("/^(\S+):\s*(.*)\s*$/", $line, $M)) {
42               $props[$M[1]] = $M[2];
43             }
44           }
45           // read the commit log.
46           while (($line = fgets($fp)) !== false) {
47             $line = rtrim($line);
48             if (strncmp($line, '    ', 4)) {
49               break;
50             }
51             $this->log[] = substr($line, 4);
52           }
53           
54           
55           do {
56             if (preg_match("/^(.+)\s+(\S+)\s*$/", $line, $M)) {
57               $st = $M[1];
58               $file = $M[2];
59               $this->files[$file] = $new;
60               $this->fileActions[$file] = $st;
61             }
62             
63           } while (($line = fgets($fp)) !== false);
64         }
65     }
66
67
68     function enumChangedOrModifiedFileNames()
69     {
70         $ret = array();
71         foreach($this->files as $f=>$com) {
72             if ($this->fileActions[$f] == 'D') {
73                 continue;
74             }
75             $ret[] = $f;
76         }
77         return $ret;
78     }
79
80     function getCommitMessage()
81     {
82         $log = join("\n", $this->log);
83         $log = preg_replace('/\[([a-fA-F0-9]+)\]/',
84           "[changeset:" . $this->repo->getBrowseRootName() . ",\$1]", $log);
85         return $log;
86     }
87
88     function getFileStream($path)
89     {
90         $rev = $this->files[$path];
91
92         // There may be a better way...
93         // ls-tree to determine the hash of the file from this change:
94         $fp = $this->run(self::$GIT, 'ls-tree', '-r', $rev, $path);
95         $line = fgets($fp);
96         $fp = null;
97         list($mode, $type, $hash, $name) = preg_split("/\s+/", $line);
98         // now we can cat that blob
99         return $this->run(self::$GIT, 'cat-file', 'blob', $hash);
100     }
101
102     function getChangesetDescriptor()
103     {
104         $cs = array();
105         foreach ($this->commits as $ref) {
106           $cs[] = '[changeset:' . $this->repo->getBrowseRootName() . ",$ref]";
107         }
108         return join(", ", $cs);
109     }
110 }