Indefero

Indefero Commit Details


Date:2008-07-26 11:42:41 (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:876e206742179ba640667430b5cad6b45c651250
Parents: 2e711bee8d2b7c6680d6d44cb796f2cc67c9ed68
Message:First work on the git browser.

Changes:

File differences

src/IDF/Git.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Git utils.
*/
class IDF_Git
{
public $repo = '';
public function __construct($repo)
{
$this->repo = $repo;
}
/**
* Given a commit hash (or a branch) returns an array of files in
* it.
*
* A file is a class with the following properties:
*
* 'perm', 'type', 'size', 'hash', 'file'
*
* @param string Commit/Branch ('HEAD')
* @param string Base folder ('')
* @return array
*/
public function filesInTree($commit='HEAD', $basefolder='')
{
if (is_object($basefolder)) {
$base = $basefolder;
} else if ($basefolder != ''
and
(
(false === ($base=$this->getFileInfo($basefolder, $commit)))
or
($base->type != 'tree')
)) {
throw new Exception(sprintf('Base folder "%s" not found.', $basefolder));
} else {
// no base
$base = (object) array('file' => '',
'hash' => $commit);
}
$res = array();
$out = array();
$cmd = sprintf('GIT_DIR=%s git-ls-tree -t -l %s', $this->repo, $base->hash);
exec($cmd, &$out);
$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);
$cm = array();
$cmd = sprintf('GIT_DIR=%s git log -1 --pretty=format:\'%%H %%at %%s\' %s -- %s', $this->repo, $commit, ($base->file) ? $base->file.'/'.$file : $file);
exec($cmd, &$cm);
list($h, $time, $log) = explode(' ', $cm[0], 3);
$res[] = (object) array('perm' => $perm, 'type' => $type,
'size' => $size, 'hash' => $hash,
'fullpath' => ($base->file) ? $base->file.'/'.$file : $file,
'log' => $log, 'time' => $time,
'file' => $file);
}
chdir($current_dir);
return $res;
}
/**
* Get the file info.
*
* @param string Tree to test
* @param string Commit/Branch ('HEAD')
* @return false or Tree information
*/
public function getFileInfo($totest, $commit='HEAD')
{
$cmd_tmpl = 'GIT_DIR=%s git-ls-tree -r -t -l %s';
$cmd = sprintf($cmd_tmpl, $this->repo, $commit);
$out = array();
exec($cmd, &$out);
foreach ($out as $line) {
list($perm, $type, $hash, $size, $file) = preg_split('/ |\t/', $line, 5, PREG_SPLIT_NO_EMPTY);
if ($totest == $file) {
return (object) array('perm' => $perm, 'type' => $type,
'size' => $size, 'hash' => $hash,
'file' => $file);
}
}
return false;
}
/**
* Get a blob.
*
* @param string Blob hash
* @return string Raw blob
*/
public function getBlob($hash)
{
return shell_exec(sprintf('GIT_DIR=%s git-cat-file blob %s',
$this->repo, $hash));
}
/**
* Get the branches.
*/
public function getBranches()
{
$out = array();
exec(sprintf('GIT_DIR=%s git branch', $this->repo), &$out);
$res = array();
foreach ($out as $b) {
$res[] = substr($b, 2);
}
return $res;
}
}
src/IDF/Views/Source.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
Pluf::loadFunction('Pluf_Shortcuts_RenderToResponse');
Pluf::loadFunction('Pluf_Shortcuts_GetObjectOr404');
Pluf::loadFunction('Pluf_Shortcuts_GetFormForModel');
/**
* View git repository.
*/
class IDF_Views_Source
{
public function index($request, $match)
{
}
public function treeBase($request, $match)
{
$title = sprintf('%s Git Source Tree', (string) $request->project);
$git = new IDF_Git(Pluf::f('git_repository'));
$branches = $git->getBranches();
$res = $git->filesInTree($match[2]);
$commit = $match[2];
return Pluf_Shortcuts_RenderToResponse('source/tree.html',
array(
'page_title' => $title,
'files' => $res,
'commit' => $commit,
'branches' => $branches,
),
$request);
}
public function tree($request, $match)
{
$title = sprintf('%s Git Source Tree', (string) $request->project);
$git = new IDF_Git(Pluf::f('git_repository'));
$branches = $git->getBranches();
$commit = $match[2];
$request_file = $match[3];
$request_file_info = $git->getFileInfo($request_file);
if (!$request_file_info) throw new Pluf_HTTP_Error404();
$bc = self::makeBreadCrumb($request->project, $commit, $request_file_info->file);
$page_title = $bc.' - '.$title;
if ($request_file_info->type != 'tree') {
return new Pluf_HTTP_Response($git->getBlob($request_file_info->hash),
'application/octet-stream');
}
$res = $git->filesInTree($commit, $request_file_info);
// try to find the previous level if it exists.
$prev = split('/', $request_file);
$l = array_pop($prev);
$previous = substr($request_file, 0, -strlen($l.' '));
return Pluf_Shortcuts_RenderToResponse('source/tree.html',
array(
'page_title' => $page_title,
'title' => $title,
'breadcrumb' => $bc,
'files' => $res,
'commit' => $commit,
'base' => $request_file_info->file,
'prev' => $previous,
'branches' => $branches,
),
$request);
}
public static function makeBreadCrumb($project, $commit, $file, $sep='/')
{
$elts = split('/', $file);
$out = array();
$stack = '';
$i = 0;
foreach ($elts as $elt) {
$stack .= ($i==0) ? $elt : '/'.$elt;
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::tree',
array($project->shortname,
$commit, $stack));
$out[] = '<a href="'.$url.'">'.Pluf_esc($elt).'</a>';
$i++;
}
return '<span class="breadcrumb">'.implode('<span class="sep">'.$sep.'</span>', $out).'</span>';
}
}
function IDF_Views_Source_PrettySize($size)
{
return Pluf_Template::markSafe(Pluf_Utils::prettySize($size));
}
src/IDF/conf/views.php
5151
5252
5353
54
55
54
55
5656
5757
5858
......
9090
9191
9292
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
93116
94117
95118
$ctl[] = array('regex' => '#^/p/(\w+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views',
'method' => 'projectHome');
'model' => 'IDF_Views_Project',
'method' => 'home');
$ctl[] = array('regex' => '#^/p/(\w+)/issues/$#',
'base' => $base,
'model' => 'IDF_Views_Issue',
'method' => 'myIssues');
// ---------- GIT ----------------------------------------
$ctl[] = array('regex' => '#^/p/(\w+)/source/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source',
'method' => 'index');
$ctl[] = array('regex' => '#^/p/(\w+)/source/tree/(\w+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source',
'method' => 'treeBase');
$ctl[] = array('regex' => '#^/p/(\w+)/source/tree/(\w+)/(.*)$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source',
'method' => 'tree');
// ---------- ADMIN --------------------------------------
$ctl[] = array('regex' => '#^/p/(\w+)/admin/$#',
'base' => $base,
'priority' => 4,
src/IDF/templates/base.html
4141
4242
4343
44
4445
46
4547
4648
4749
<div id="header">
<div id="main-tabs">
{if $project}
{* <a href="{url 'IDF_Views_Project::home', array($project.shortname)}"{block tabhome}{/block}>{trans 'Project Home'}</a> *}
<a href="{url 'IDF_Views_Issue::index', array($project.shortname)}"{block tabissues}{/block}>{trans 'Issues'}</a>
<a href="{url 'IDF_Views_Source::index', array($project.shortname)}"{block tabsource}{/block}>{trans 'Source'}</a>
{if $isOwner}
<a href="{url 'IDF_Views_Project::admin', array($project.shortname)}"{block tabadmin}{/block}>{trans 'Administer'}</a>{/if}{/if}
</div>
src/IDF/templates/source/base.html
1
2
3
4
5
6
7
8
{extends "base.html"}
{block tabsource} class="active"{/block}
{block subtabs}
<div id="sub-tabs">
{trans 'Source Tree'}
</div>
{/block}
{block title}{$title}{/block}
src/IDF/templates/source/tree.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
{extends "source/base.html"}
{block docclass}yui-t1{/block}
{block body}
<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 colspan="2">{trans 'File'}</th>
<th>{trans 'Age'}</th>
<th>{trans 'Message'}</th>
<th>{trans 'Size'}</th>
</tr>
</thead>
<tbody>
{if $base}
<tr>
<td>&nbsp;</td>
<td colspan="4">
<a href="{url 'IDF_Views_Source::tree', array($project.shortname, $commit, $prev)}">..</a></td>
</tr>
{/if}
{foreach $files as $file}
{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><a href="{$url}">{$file.file}</a></td>
<td><span class="smaller">{$file.time|timeago:"wihtout"}</span></td>
<td{if $file.type != 'blob'} colspan="2"{/if}><span class="smaller">{$file.log}</span></td>
{if $file.type == 'blob'}
<td>{$file.size|size}</td>{/if}
</tr>
{/foreach}
</tbody>
</table>
{/block}
{block context}
<p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
<span class="label"><a href="{$url}" class="label">{$branch}</a></span><br />
{/foreach}
</p>
{/block}
www/media/idf/css/style.css
210210
211211
212212
213
214
215
216
213217
214218
215219
......
241245
242246
243247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
244273
245274
246275
clear: both;
}
.sep {
margin: 0 0.3em;
}
/**
* Tabs
*/
#sub-tabs a.active {
text-decoration: none;
}
/**
* Tree list
*/
table.tree-list {
width: 100%;
}
table.tree-list th {
background-color: #e4e8E0;
vertical-align: top;
border-color: #d3d7cf;
}
table.tree-list tr {
border-left: 1px solid #d3d7cf;
border-right: 1px solid #d3d7cf;
border-bottom: 1px solid #d3d7cf;
}
table.tree-list td {
border: none;
vertical-align: top;
}
/**
* Autocomplete.

Archive Download the corresponding diff file

Page rendered in 0.10718s using 13 queries.