Indefero

Indefero Commit Details


Date:2008-07-27 10:43:51 (16 years 4 months ago)
Author:Loic d'Anterroches
Branch:dev, develop, feature-issue_links, feature.better-home, feature.content-md5, feature.diff-whitespace, feature.download-md5, feature.issue-links, feature.issue-of-others, feature.issue-summary, feature.search-filter, feature.webrepos, feature.wiki-default-page, master, newdiff, release-1.1, release-1.2, release-1.3, svn
Commit:b6084cbf070bf9e78101b1088d5d6c0d852ed6dc
Parents: 9a2b8e249acad9f84401846cf87bf040ffd585b3
Message:Improved the support of the git browser.

Now the reference is always a commit as from a commit it is always possible to travel back to the corresponding tree and blobs.
Changes:

File differences

src/IDF/Git.php
2323
2424
2525
26
2627
2728
2829
2930
31
3032
3133
3234
3335
3436
3537
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
3656
37
38
57
3958
4059
4160
4261
4362
44
63
4564
4665
4766
48
67
4968
50
51
52
53
54
55
56
57
58
59
60
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
6188
62
63
64
89
6590
66
6791
68
69
70
92
93
7194
72
73
74
75
76
95
96
97
98
99
77100
78
79
80
81
101
102
103
82104
83
84
85
105
86106
87
88
89
107
108
109
110
111
112
90113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
91142
92143
93
94
95144
96145
97
98146
99147
100148
149
101150
102151
103152
104153
105
154
106155
107156
108
157
109158
110159
111
160
161
162
112163
113164
114165
......
131182
132183
133184
134
185
186
135187
136188
137189
......
142194
143195
144196
145
197
198
146199
147200
148201
......
158211
159212
160213
161
162
214
215
216
217
163218
164219
165220
......
175230
176231
177232
178
233
179234
180235
181236
......
184239
185240
186241
187
242
188243
189244
190245
191
246
192247
193
194248
195249
196250
197
251
252
198253
199254
200
201255
202256
203257
/**
* Git utils.
*
*/
class IDF_Git
{
public $repo = '';
public $mediumtree_fmt = 'commit %H%nAuthor: %an <%ae>%nTree: %T%nDate: %ai%n%n%s%n%n%b';
public function __construct($repo)
{
$this->repo = $repo;
}
/**
* Test a given object hash.
*
* @param string Object hash.
* @return mixed false if not valid or 'blob', 'tree', 'commit'
*/
public function testHash($hash)
{
$cmd = sprintf('GIT_DIR=%s git cat-file -t %s',
escapeshellarg($this->repo),
escapeshellarg($hash));
$ret = 0; $out = array();
exec($cmd, &$out, &$ret);
if ($ret != 0) return false;
return trim($out[0]);
}
/**
* Given a commit hash (or a branch) returns an array of files in
* it.
* Given a commit hash returns an array of files in it.
*
* A file is a class with the following properties:
*
* 'perm', 'type', 'size', 'hash', 'file'
*
* @param string Tree ('HEAD')
* @param string Commit ('HEAD')
* @param string Base folder ('')
* @return array
*/
public function filesInTree($tree='HEAD', $basefolder='')
public function filesAtCommit($commit='HEAD', $folder='')
{
if (is_object($basefolder)) {
$base = $basefolder;
} else if (
$basefolder != ''
and
(
(false === ($base=$this->getFileInfo($basefolder, $tree)))
or
($base->type != 'tree')
)) {
throw new Exception(sprintf('Base folder "%s" not found.', $basefolder));
if ('commit' != $this->testHash($commit)) {
throw new Exception(sprintf(__('Not a valid commit: %s.'), $commit));
}
// now we grab the info about this commit including its tree.
$co = $this->getCommit($commit);
if ($folder) {
// As we are limiting to a given folder, we need to find
// the tree corresponding to this folder.
$found = false;
foreach ($this->getTreeInfo($co->tree) as $file) {
if ($file->type == 'tree' and $file->file == $folder) {
$found = true;
$tree = $file->hash;
break;
}
}
if (!$found) {
throw new Exception(sprintf(__('Folder %1$s not found in commit %2$s.'), $folder, $commit));
}
} else {
// no base
$base = (object) array('file' => '',
'hash' => $tree);
$tree = $co->tree;
}
$res = array();
$out = array();
$cmd = sprintf('GIT_DIR=%s git-ls-tree -t -l %s', $this->repo, $base->hash);
exec($cmd, &$out);
// get the raw log corresponding to this commit to find the
// origin of each file.
$rawlog = array();
foreach ($this->getBranches() as $br) {
$cmd = sprintf('GIT_DIR=%s git log --raw --abbrev=40 --pretty=oneline %s',
$this->repo, $br);
exec($cmd, &$rawlog);
}
$cmd = sprintf('GIT_DIR=%s git log --raw --abbrev=40 --pretty=oneline %s',
escapeshellarg($this->repo), escapeshellarg($commit));
exec($cmd, &$rawlog);
// We reverse the log to be able to use a fixed efficient
// regex without back tracking.
$rawlog = implode("\n", array_reverse($rawlog));
$current_dir = getcwd();
chdir(substr($this->repo, 0, -5));
foreach ($out as $line) {
list($perm, $type, $hash, $size, $file) = preg_split('/ |\t/', $line, 5, PREG_SPLIT_NO_EMPTY);
foreach ($this->getTreeInfo($tree, false) as $file) {
// Now we grab the files in the current tree with as much
// information as possible.
$matches = array();
$date = '1970-01-01 12:00:00';
$log = '';
if ($type == 'blob' and preg_match('/^\:\d{6} \d{6} [0-9a-f]{40} '.$hash.' .*^([0-9a-f]{40})/msU',
if ($file->type == 'blob' and preg_match('/^\:\d{6} \d{6} [0-9a-f]{40} '.$file->hash.' .*^([0-9a-f]{40})/msU',
$rawlog, &$matches)) {
$_c = $this->getCommit($matches[1]);
$date = $_c->date;
$log = $_c->title;
$fc = $this->getCommit($matches[1]);
$file->date = $fc->date;
$file->log = $fc->title;
} else if ($file->type == 'blob') {
$file->date = $co->date;
$file->log = $co->title;
}
$file->fullpath = ($folder) ? $folder.'/'.$file->file : $file->file;
$res[] = $file;
}
return $res;
}
/**
* Get the tree info.
*
* @param string Tree hash
* @param bool Do we recurse in subtrees (true)
* @return array Array of file information.
*/
public function getTreeInfo($tree, $recurse=true)
{
if ('tree' != $this->testHash($tree)) {
throw new Exception(sprintf(__('Not a valid tree: %s.'), $tree));
}
$cmd_tmpl = 'GIT_DIR=%s git-ls-tree%s -t -l %s';
$cmd = sprintf($cmd_tmpl,
escapeshellarg($this->repo),
($recurse) ? ' -r' : '',
escapeshellarg($tree));
$out = array();
$res = array();
exec($cmd, &$out);
foreach ($out as $line) {
list($perm, $type, $hash, $size, $file) = preg_split('/ |\t/', $line, 5, PREG_SPLIT_NO_EMPTY);
$res[] = (object) array('perm' => $perm, 'type' => $type,
'size' => $size, 'hash' => $hash,
'fullpath' => ($base->file) ? $base->file.'/'.$file : $file,
'log' => $log, 'date' => $date,
'file' => $file);
}
chdir($current_dir);
return $res;
}
/**
* Get the file info.
*
* @param string File
* @param string Tree ('HEAD')
* @param string Commit ('HEAD')
* @return false Information
*/
public function getFileInfo($totest, $tree='HEAD')
public function getFileInfo($totest, $commit='HEAD')
{
$cmd_tmpl = 'GIT_DIR=%s git-ls-tree -r -t -l %s';
$cmd = sprintf($cmd_tmpl, $this->repo, $tree);
$cmd = sprintf($cmd_tmpl,
escapeshellarg($this->repo),
escapeshellarg($commit));
$out = array();
exec($cmd, &$out);
foreach ($out as $line) {
public function getBlob($hash)
{
return shell_exec(sprintf('GIT_DIR=%s git-cat-file blob %s',
$this->repo, $hash));
escapeshellarg($this->repo),
escapeshellarg($hash)));
}
/**
public function getBranches()
{
$out = array();
exec(sprintf('GIT_DIR=%s git branch', $this->repo), &$out);
exec(sprintf('GIT_DIR=%s git branch',
escapeshellarg($this->repo)), &$out);
$res = array();
foreach ($out as $b) {
$res[] = substr($b, 2);
*/
public function getCommit($commit='HEAD')
{
$cmd = sprintf('GIT_DIR=%s git show --date=iso --pretty=medium %s',
escapeshellarg($this->repo), $commit);
$cmd = sprintf('GIT_DIR=%s git show --date=iso --pretty=format:%s %s',
escapeshellarg($this->repo),
"'".$this->mediumtree_fmt."'",
escapeshellarg($commit);
$out = array();
exec($cmd, &$out);
$log = array();
$log[] = $line;
}
}
$out = self::parseLog($log);
$out = self::parseLog($log, 4);
$out[0]->changes = $change;
return $out[0];
}
/**
* Get latest changes.
*
* @param string Tree ('HEAD').
* @param string Commit ('HEAD').
* @param int Number of changes (10).
* @return array Changes.
*/
public function getChangeLog($tree='HEAD', $n=10)
public function getChangeLog($commit='HEAD', $n=10)
{
$format = 'commit %H%nAuthor: %an <%ae>%nTree: %T%nDate: %ai%n%n%s%n%n%b';
if ($n === null) $n = '';
else $n = ' -'.$n;
$cmd = sprintf('GIT_DIR=%s git log%s --date=iso --pretty=format:\'%s\' %s',
escapeshellarg($this->repo), $n, $format, $tree);
escapeshellarg($this->repo), $n, $this->mediumtree_fmt,
escapeshellarg($commit));
$out = array();
exec($cmd, &$out);
//print_r($cmd);
return self::parseLog($out, 4);
}
src/IDF/Views/Source.php
5353
5454
5555
56
5657
57
58
59
60
61
62
63
64
65
66
67
68
58
59
60
6961
7062
7163
7264
7365
7466
75
67
7668
7769
7870
......
8375
8476
8577
86
78
8779
88
80
8981
9082
9183
9284
9385
94
86
9587
9688
97
98
99
89
90
91
10092
10193
10294
10395
104
105
106
107
108
109
110
111
11296
11397
11498
11599
116100
117101
118
102
119103
120104
121105
......
125109
126110
127111
128
112
129113
130114
131115
......
135119
136120
137121
138
122
139123
140124
141125
{
$title = sprintf('%s Git Source Tree', (string) $request->project);
$git = new IDF_Git(Pluf::f('git_repository'));
$commit = $match[2];
$branches = $git->getBranches();
$res = $git->filesInTree($match[2]);
$tree = $match[2];
$cobject = '';
$tree_in = in_array($tree, $branches);
foreach ($branches as $br) {
foreach ($git->getChangeLog($br, null) as $change) {
if ($change->tree == $tree) {
$cobject = $change;
break 2;
}
}
}
$res = $git->filesAtCommit($commit);
$cobject = $git->getCommit($commit);
$tree_in = in_array($commit, $branches);
return Pluf_Shortcuts_RenderToResponse('source/tree.html',
array(
'page_title' => $title,
'title' => $title,
'files' => $res,
'cobject' => $cobject,
'tree' => $tree,
'commit' => $commit,
'tree_in' => $tree_in,
'branches' => $branches,
),
{
$title = sprintf('%s Git Source Tree', (string) $request->project);
$git = new IDF_Git(Pluf::f('git_repository'));
$tree = $match[2];
$commit = $match[2];
$request_file = $match[3];
$request_file_info = $git->getFileInfo($request_file, $tree);
$request_file_info = $git->getFileInfo($request_file, $commit);
if (!$request_file_info) throw new Pluf_HTTP_Error404();
if ($request_file_info->type != 'tree') {
return new Pluf_HTTP_Response($git->getBlob($request_file_info->hash),
'application/octet-stream');
}
$bc = self::makeBreadCrumb($request->project, $tree, $request_file_info->file);
$bc = self::makeBreadCrumb($request->project, $commit, $request_file_info->file);
$page_title = $bc.' - '.$title;
$branches = $git->getBranches();
$cobject = '';
$tree_in = in_array($tree, $branches);
$res = $git->filesInTree($tree, $request_file_info);
$cobject = $git->getCommit();
$tree_in = in_array($commit, $branches);
$res = $git->filesAtCommit($commit, $request_file);
// try to find the previous level if it exists.
$prev = split('/', $request_file);
$l = array_pop($prev);
$previous = substr($request_file, 0, -strlen($l.' '));
foreach ($branches as $br) {
foreach ($git->getChangeLog($br, null) as $change) {
if ($change->tree == $tree) {
$cobject = $change;
break 2;
}
}
}
return Pluf_Shortcuts_RenderToResponse('source/tree.html',
array(
'page_title' => $page_title,
'title' => $title,
'breadcrumb' => $bc,
'files' => $res,
'tree' => $tree,
'commit' => $commit,
'cobject' => $cobject,
'base' => $request_file_info->file,
'prev' => $previous,
$request);
}
public static function makeBreadCrumb($project, $tree, $file, $sep='/')
public static function makeBreadCrumb($project, $commit, $file, $sep='/')
{
$elts = split('/', $file);
$out = array();
$stack .= ($i==0) ? $elt : '/'.$elt;
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::tree',
array($project->shortname,
$tree, $stack));
$commit, $stack));
$out[] = '<a href="'.$url.'">'.Pluf_esc($elt).'</a>';
$i++;
}
src/IDF/templates/source/base.html
22
33
44
5
6
5
6
77
88
99
{block tabsource} class="active"{/block}
{block subtabs}
<div id="sub-tabs">
{trans 'Source Tree'} |
<a href="{url 'IDF_Views_Source::changeLog', array($project.shortname, 'master')}">{trans 'Change Log'}</a>
<a {if $inSourceTree}class="active" {/if}href="{url 'IDF_Views_Source::treeBase', array($project.shortname, 'master')}">{trans 'Source Tree'}</a> |
<a {if $inChangeLog}class="active" {/if}href="{url 'IDF_Views_Source::changeLog', array($project.shortname, 'master')}">{trans 'Change Log'}</a>
</div>
{/block}
{block title}{$title}{/block}
src/IDF/templates/source/changelog.html
11
2
2
33
44
55
66
77
88
9
9
1010
1111
1212
1313
14
15
14
15
1616
17
18
19
20
17
18
19
20
21
22
23
24
25
26
27
28
2129
2230
2331
{extends "source/base.html"}
{block docclass}yui-t1{/block}
{block docclass}yui-t1{assign $inChangeLog=true}{/block}
{block body}
<table summary="" class="tree-list">
<thead>
<tr>
<th>{trans 'Age'}</th>
<th>{trans 'Message'}</th>
<th>{trans 'Details'}</th>
{* <th>{trans 'Details'}</th> *}
</tr>
</thead>
<tbody>
{foreach $changes as $change}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $change.tree)}
<tr>
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $change.commit)}
<tr class="log">
<td><a href="{$url}">{$change.date|dateago:"wihtout"}</a></td>
<td>{$change.title}{if $change.full_message}<br /><span class="smaller">{$change.full_message}</span>{/if}</td>
<td><span class="smaller">{trans 'Tree:'}&nbsp;<a href="{$url}">{$change.tree}</a><br />
{trans 'By:'} {$change.author|strip_tags} {* this remove the email address *}
</span></td>
<td>{$change.title}{if $change.full_message}<br /><br />{$change.full_message}{/if}</td>
</tr>
<tr class="extra">
<td colspan="2">
<div class="helptext right">{trans 'Commit:'}&nbsp;<a href="{$url}" class="mono">{$change.commit}</a>,
{trans 'by'} {$change.author|strip_tags} {* this remove the email address *}
</div>
</td>
{* <td><span class="smaller">{trans 'Tree:'}&nbsp;<a href="{$url}">{$change.commit}</a><br />
{trans 'By:'} {$change.author|strip_tags}
</span></td> *}
</tr>
{/foreach}
</tbody>
src/IDF/templates/source/tree.html
11
2
2
33
4
4
55
66
77
......
1111
1212
1313
14
14
1515
16
16
17
18
1719
1820
1921
2022
2123
22
23
24
25
26
2427
2528
2629
27
30
2831
29
32
3033
3134
35
3236
3337
38
3439
3540
3641
......
4247
4348
4449
45
50
4651
4752
4853
{extends "source/base.html"}
{block docclass}yui-t1{/block}
{block docclass}yui-t1{assign $inSourceTree=true}{/block}
{block body}
<h2><a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $tree)}">{trans 'Root'}</a><span class="sep">/</span>{if $breadcrumb}{$breadcrumb|safe}{/if}</h2>
<h2><a href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $commit)}">{trans 'Root'}</a><span class="sep">/</span>{if $breadcrumb}{$breadcrumb|safe}{/if}</h2>
<table summary="" class="tree-list">
<thead>
<tr>
<th>{trans 'Size'}</th>
</tr>
</thead>{if !$tree_in}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $tree)}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $commit)}
<tfoot>
<tr><th colspan="5">{blocktrans}In tree <a href="{$url}">{$tree}</a> created {$cobject.date|dateago}.{/blocktrans}</th></tr>
<tr><th colspan="5">{blocktrans}In commit <a class="mono" href="{$url}">{$commit}</a> created {$cobject.date|dateago}.{/blocktrans}<br />
<span class="smaller">{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans}</span>
</th></tr>
</tfoot>
{/if}<tbody>
{if $base}
<tr>
<td>&nbsp;</td>
<td colspan="4">
<a href="{url 'IDF_Views_Source::tree', array($project.shortname, $tree, $prev)}">..</a></td>
<td>
<a href="{url 'IDF_Views_Source::tree', array($project.shortname, $commit, $prev)}">..</a></td>
<td colspan="3"></td>
</tr>
{/if}
{foreach $files as $file}
{aurl 'url', 'IDF_Views_Source::tree', array($project.shortname, $tree, $file.fullpath)}
{aurl 'url', 'IDF_Views_Source::tree', array($project.shortname, $commit, $file.fullpath)}
<tr>
<td><img src="{media '/idf/img/'~$file.type~'.png'}" alt="{$file.type}" /></td>
<td class="fileicon"><img src="{media '/idf/img/'~$file.type~'.png'}" alt="{$file.type}" /></td>
<td{if $file.type != 'blob'} colspan="4"{/if}><a href="{$url}">{$file.file}</a></td>
{if $file.type == 'blob'}
{if isset($file.date)}
<td><span class="smaller">{$file.date|dateago:"wihtout"}</span></td>
<td><span class="smaller">{$file.log}</span></td>
{else}<td colspan="2"></td>{/if}
<td>{$file.size|size}</td>{/if}
</tr>
{/foreach}
<p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
<span class="label{if $tree == $branch} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
<span class="label{if $commit == $branch} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
{/foreach}
</p>
{/block}
www/media/idf/css/style.css
2323
2424
2525
26
27
28
29
30
31
32
33
2634
2735
2836
......
259267
260268
261269
262
270
263271
264272
265273
......
278286
279287
280288
281
289
282290
283291
284292
285293
294
295
296
297
298
299
300
301
302
303
304
305
286306
307
287308
288309
289310
padding: 0 1em;
}
.right {
text-align: right;
}
.mono {
font-family: monospace;
}
div.context {
padding-left: 1em;
}
table.tree-list th {
background-color: #e4e8E0;
vertical-align: top;
border-color: #d3d7cf;
border-color: #d3d7cf;
}
table.tree-list tr {
font-weight: normal;
}
table.recent-issues tfoot th a {
table.tree-list tfoot th a {
color: #000;
font-weight: normal;
}
table.tree-list tr.log {
border-bottom: 1px solid #e7ebe3;
/* background-color: #eef2ea !important; */
}
table.tree-list tr.extra {
/* border-bottom: 1px solid #e7ebe3; */
/* background-color: #eef2ea !important; */
}
table td.fileicon {
width: 20px;
}
/**
* Autocomplete.

Archive Download the corresponding diff file

Page rendered in 0.10120s using 13 queries.