Skip to content

Commit cc7ed0c

Browse files
mattyj2001dregad
authored andcommitted
Initial checkin of SourceGitphp on 1.2.x branch
Fixes #197 Signed-off-by: Damien Regad <[email protected]>
1 parent a4a80c6 commit cc7ed0c

File tree

8 files changed

+475
-0
lines changed

8 files changed

+475
-0
lines changed

SourceGitphp/LICENSE

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Copyright (c) 2012 John Reese
2+
3+
Permission is hereby granted, free of charge, to any person
4+
obtaining a copy of this software and associated documentation
5+
files (the "Software"), to deal in the Software without
6+
restriction, including without limitation the rights to use,
7+
copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the
9+
Software is furnished to do so, subject to the following
10+
conditions:
11+
12+
The above copyright notice and this permission notice shall be
13+
included in all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
OTHER DEALINGS IN THE SOFTWARE.
23+

SourceGitphp/SourceGitphp.php

Lines changed: 367 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,367 @@
1+
<?php
2+
3+
# Copyright (c) 2012 John Reese
4+
# Licensed under the MIT license
5+
6+
if ( false === include_once( config_get( 'plugin_path' ) . 'Source/MantisSourcePlugin.class.php' ) ) {
7+
return;
8+
}
9+
10+
require_once( config_get( 'core_path' ) . 'url_api.php' );
11+
12+
class SourceGitphpPlugin extends MantisSourcePlugin {
13+
public function register() {
14+
$this->name = plugin_lang_get( 'title' );
15+
$this->description = plugin_lang_get( 'description' );
16+
17+
$this->version = '0.10';
18+
$this->requires = array(
19+
'MantisCore' => '1.2.0',
20+
'Source' => '0.16',
21+
);
22+
23+
$this->author = 'John Reese';
24+
$this->contact = '[email protected]';
25+
$this->url = 'http://noswap.com';
26+
}
27+
28+
public $type = 'gitphp';
29+
30+
public function show_type() {
31+
return plugin_lang_get( 'gitphp' );
32+
}
33+
34+
public function show_changeset( $p_repo, $p_changeset ) {
35+
$t_ref = substr( $p_changeset->revision, 0, 8 );
36+
$t_branch = $p_changeset->branch;
37+
38+
return "$t_branch $t_ref";
39+
}
40+
41+
public function show_file( $p_repo, $p_changeset, $p_file ) {
42+
return "$p_file->action - $p_file->filename";
43+
}
44+
45+
private function uri_base( $p_repo ) {
46+
$t_uri_base = $p_repo->info['gitphp_root'] . '?p=' . $p_repo->info['gitphp_project'] . '&';
47+
return $t_uri_base;
48+
}
49+
50+
public function url_repo( $p_repo, $t_changeset=null ) {
51+
return $this->uri_base( $p_repo ) . ( $t_changeset ? 'h=' . $t_changeset->revision : '' );
52+
}
53+
54+
public function url_changeset( $p_repo, $p_changeset ) {
55+
return $this->uri_base( $p_repo ) . 'a=commitdiff&h=' . $p_changeset->revision;
56+
}
57+
58+
public function url_file( $p_repo, $p_changeset, $p_file ) {
59+
return $this->uri_base( $p_repo ) . 'a=blob&f=' . $p_file->filename .
60+
'&h=' . $p_file->revision . '&hb=' . $p_changeset->revision;
61+
}
62+
63+
public function url_diff( $p_repo, $p_changeset, $p_file ) {
64+
return $this->uri_base( $p_repo ) . 'a=blobdiff&f=' . $p_file->filename .
65+
'&h=' . $p_file->revision . '&hb=' . $p_changeset->revision . '&hp=' . $p_changeset->parent;
66+
}
67+
68+
public function update_repo_form( $p_repo ) {
69+
$t_gitphp_root = null;
70+
$t_gitphp_project = null;
71+
72+
if ( isset( $p_repo->info['gitphp_root'] ) ) {
73+
$t_gitphp_root = $p_repo->info['gitphp_root'];
74+
}
75+
76+
if ( isset( $p_repo->info['gitphp_project'] ) ) {
77+
$t_gitphp_project = $p_repo->info['gitphp_project'];
78+
}
79+
80+
if ( isset( $p_repo->info['master_branch'] ) ) {
81+
$t_master_branch = $p_repo->info['master_branch'];
82+
} else {
83+
$t_master_branch = 'master';
84+
}
85+
?>
86+
<tr <?php echo helper_alternate_class() ?>>
87+
<td class="category"><?php echo plugin_lang_get( 'gitphp_root' ) ?></td>
88+
<td><input name="gitphp_root" maxlength="250" size="40" value="<?php echo string_attribute( $t_gitphp_root ) ?>"/></td>
89+
</tr>
90+
<tr <?php echo helper_alternate_class() ?>>
91+
<td class="category"><?php echo plugin_lang_get( 'gitphp_project' ) ?></td>
92+
<td><input name="gitphp_project" maxlength="250" size="40" value="<?php echo string_attribute( $t_gitphp_project ) ?>"/></td>
93+
</tr>
94+
<tr <?php echo helper_alternate_class() ?>>
95+
<td class="category"><?php echo plugin_lang_get( 'master_branch' ) ?></td>
96+
<td><input name="master_branch" maxlength="250" size="40" value="<?php echo string_attribute( $t_master_branch ) ?>"/></td>
97+
</tr>
98+
<?php
99+
}
100+
101+
public function update_repo( $p_repo ) {
102+
$f_gitphp_root = gpc_get_string( 'gitphp_root' );
103+
$f_gitphp_project = gpc_get_string( 'gitphp_project' );
104+
$f_master_branch = gpc_get_string( 'master_branch' );
105+
106+
$p_repo->info['gitphp_root'] = $f_gitphp_root;
107+
$p_repo->info['gitphp_project'] = $f_gitphp_project;
108+
$p_repo->info['master_branch'] = $f_master_branch;
109+
110+
return $p_repo;
111+
}
112+
113+
public function precommit( ) {
114+
# TODO: Implement real commit sequence.
115+
return;
116+
}
117+
118+
public function commit( $p_repo, $p_data ) {
119+
# Handle branch names with '+' character
120+
$p_data = str_replace('_plus_', '+', $p_data);
121+
122+
# The -d option from curl requires you to encode your own data.
123+
# Once it reaches here it is decoded. Hence we split by a space
124+
# were as the curl command uses a '+' character instead.
125+
# i.e. DATA=`echo $INPUT | sed -e 's/ /+/g'`
126+
list ( , $t_commit_id, $t_branch) = explode(' ', $p_data);
127+
list ( , , $t_branch) = explode('/', $t_branch, 3);
128+
129+
# master_branch contains comma-separated list of branches
130+
$t_branches = explode(',', $p_repo->info['master_branch']);
131+
if (!in_array('*', $t_branches) and !in_array($t_branch, $t_branches))
132+
{
133+
return;
134+
}
135+
136+
return $this->import_commits($p_repo, null, $t_commit_id, $t_branch);
137+
}
138+
139+
public function import_full( $p_repo ) {
140+
echo '<pre>';
141+
$t_branch = $p_repo->info['master_branch'];
142+
if ( is_blank( $t_branch ) ) {
143+
$t_branch = 'master';
144+
}
145+
146+
if ($t_branch != '*')
147+
{
148+
$t_branches = array_map( 'trim', explode( ',', $t_branch ) );
149+
}
150+
else
151+
{
152+
$t_heads_url = $this->uri_base( $p_repo ) . 'a=heads';
153+
$t_branches_input = url_get( $t_heads_url );
154+
155+
$t_branches_input = str_replace( array("\r", "\n", '&lt;', '&gt;', '&nbsp;'), array('', '', '<', '>', ' '), $t_branches_input );
156+
157+
$t_branches_input_p1 = strpos( $t_branches_input, '<table class="heads">' );
158+
$t_branches_input_p2 = strpos( $t_branches_input, '<div class="page_footer">' );
159+
$t_gitphp_heads = substr( $t_branches_input, $t_branches_input_p1, $t_branches_input_p2 - $t_branches_input_p1 );
160+
preg_match_all( '/<a class="list name".*>(.*)<\/a>/iU', $t_gitphp_heads, $t_matches, PREG_SET_ORDER );
161+
162+
$t_branches = array();
163+
foreach ($t_matches as $match)
164+
{
165+
$t_branch = trim($match[1]);
166+
if ($match[1] != 'origin' and !in_array($t_branch,$t_branches))
167+
{
168+
$t_branches[] = $t_branch;
169+
}
170+
}
171+
}
172+
173+
$t_changesets = array();
174+
175+
$t_changeset_table = plugin_table( 'changeset', 'Source' );
176+
177+
foreach( $t_branches as $t_branch ) {
178+
$t_query = "SELECT parent FROM $t_changeset_table
179+
WHERE repo_id=" . db_param() . ' AND branch=' . db_param() .
180+
'ORDER BY timestamp ASC';
181+
$t_result = db_query_bound( $t_query, array( $p_repo->id, $t_branch ), 1 );
182+
183+
$t_commits = array( $t_branch );
184+
185+
if ( db_num_rows( $t_result ) > 0 ) {
186+
$t_parent = db_result( $t_result );
187+
echo "Oldest '$t_branch' branch parent: '$t_parent'\n";
188+
189+
if ( !empty( $t_parent ) ) {
190+
$t_commits[] = $t_parent;
191+
}
192+
}
193+
194+
$t_changesets = array_merge( $t_changesets, $this->import_commits( $p_repo, $this->uri_base( $p_repo ), $t_commits, $t_branch ) );
195+
}
196+
197+
echo '</pre>';
198+
199+
return $t_changesets;
200+
}
201+
202+
public function import_latest( $p_repo ) {
203+
return $this->import_full( $p_repo );
204+
}
205+
206+
private function import_commits( $p_repo, $p_uri_base, $p_commit_ids, $p_branch='' ) {
207+
static $s_parents = array();
208+
static $s_counter = 0;
209+
210+
if ( is_array( $p_commit_ids ) ) {
211+
$s_parents = array_merge( $s_parents, $p_commit_ids );
212+
} else {
213+
$s_parents[] = $p_commit_ids;
214+
}
215+
216+
$t_changesets = array();
217+
218+
while( count( $s_parents ) > 0 && $s_counter < 200 ) {
219+
$t_commit_id = array_shift( $s_parents );
220+
221+
echo "Retrieving $t_commit_id ...\n ";
222+
223+
# Handle branch names with '+' character
224+
$t_fixed_id = str_replace('+', '%2B', $t_commit_id);
225+
$t_commit_url = $this->uri_base( $p_repo ) . 'a=commit&h=' . $t_fixed_id;
226+
$t_input = url_get( $t_commit_url );
227+
228+
if ( !$t_input ) {
229+
echo "failed.\n";
230+
echo "'$t_commit_url' did not return any data.\n";
231+
die();
232+
}
233+
234+
list( $t_changeset, $t_commit_parents ) = $this->commit_changeset( $p_repo, $t_input, $p_branch );
235+
if ( !is_null( $t_changeset ) ) {
236+
$t_changesets[] = $t_changeset;
237+
}
238+
239+
$s_parents = array_merge( $s_parents, $t_commit_parents );
240+
$s_counter += 1;
241+
}
242+
243+
$s_counter = 0;
244+
return $t_changesets;
245+
}
246+
247+
private function commit_changeset( $p_repo, $p_input, $p_branch='' ) {
248+
249+
$t_input = str_replace( array("\r", "\n", '&lt;', '&gt;', '&nbsp;'), array('', '', '<', '>', ' '), $p_input );
250+
251+
# Extract sections of commit data and changed files
252+
$t_input_p1 = strpos( $t_input, '<div class="title">' );
253+
$t_input_p2 = strpos( $t_input, '<div class="list_head">' );
254+
if ( false === $t_input_p1 || false === $t_input_p2 ) {
255+
echo "commit data failure.\n";
256+
var_dump( strlen( $t_input ), $t_input_p1, $t_input_p2 );
257+
die();
258+
}
259+
$t_gitphp_data = substr( $t_input, $t_input_p1, $t_input_p2 - $t_input_p1 );
260+
261+
$t_input_p1 = strpos( $t_input, '<table class="diff_tree">' );
262+
263+
if ( false === $t_input_p1 ) {
264+
$t_input_p1 = strpos( $t_input, '<tr class="light">' );
265+
if ( false === $t_input_p1 ) {
266+
$t_input_p1 = strpos( $t_input, '<tr class="dark">' );
267+
}
268+
}
269+
270+
$t_input_p2 = strpos( $t_input, '<div class="page_footer">' );
271+
272+
if ( false === $t_input_p1 || false === $t_input_p2 ) {
273+
echo 'file data failure.';
274+
var_dump( strlen( $t_input ), $t_input_p1, $t_input_p2 );
275+
print_r($t_input);
276+
die();
277+
}
278+
279+
$t_gitphp_files = substr( $t_input, $t_input_p1, $t_input_p2 - $t_input_p1 );
280+
281+
282+
# Get commit revsion and make sure it's not a dupe
283+
preg_match( '#<td class="monospace">([a-f0-9]*)#', $t_gitphp_data, $t_matches );
284+
285+
$t_commit['revision'] = $t_matches[1];
286+
287+
echo "processing $t_commit[revision] ... \n";
288+
289+
if ( !SourceChangeset::exists( $p_repo->id, $t_commit['revision'] ) ) {
290+
291+
# Parse for commit data
292+
# m modifier means "make . include newlines"
293+
# x modifier means "ignore whitespace"
294+
preg_match( '#<td>author</td>.*?<td>(.*?)<.*?<time.*?>(.*?)<.*?<td>committer</td>.*?<td>(.*?)<.*<div.class="page_body">(.*?)</div>.*#mx',
295+
$t_gitphp_data, $t_matches );
296+
297+
$t_commit['author'] = $t_matches[1];
298+
# gitphp doesn't parse email address for some reason?
299+
$t_commit['author_email'] = "n/a";
300+
$t_commit['date'] = date( 'Y-m-d H:i:s', strtotime( $t_matches[2] ) );
301+
$t_commit['committer'] = $t_matches[3];
302+
$t_commit['committer_email'] = "n/a";
303+
$t_commit['message'] = trim( preg_replace( '#<br.*?>#', PHP_EOL, $t_matches[4] ) );
304+
305+
$t_parents = array();
306+
307+
if ( preg_match_all( '#parent</td>.*?<td.class="monospace">.*?<a.*?>(.*?)<.*#mx', $t_gitphp_data, $t_matches ) ) {
308+
foreach( $t_matches[1] as $t_match ) {
309+
$t_parents[] = $t_commit['parent'] = $t_match;
310+
}
311+
}
312+
313+
# Strip ref links and signoff spans from commit message
314+
$t_commit['message'] = preg_replace( array( '#<a[^>]*>([^<]*)</a>#', '#<span[^>]*>(.*?)</span>#' ),
315+
'$1', $t_commit['message'] );
316+
317+
# Prepend a # sign to mantis number
318+
$t_commit['message'] = preg_replace( '#(mantis)\s+(\d+)#i', '$1 #$2',$t_commit['message'] );
319+
320+
# Parse for changed file data
321+
$t_commit['files'] = array();
322+
323+
preg_match_all( '#class="list".*?h=(\w*).*?f=(.*?)".*?<.a>#',
324+
$t_gitphp_files, $t_matches, PREG_SET_ORDER );
325+
326+
foreach( $t_matches as $t_file_matches ) {
327+
$t_file = array();
328+
$t_file['filename'] = str_replace('%2F', '/', $t_file_matches[2]);
329+
$t_file['revision'] = $t_file_matches[1];
330+
331+
if ( isset( $t_file_matches[3] ) ) {
332+
if ( $t_file_matches[3] == 'new' or $t_file_matches[3] == 'moved' ) {
333+
$t_file['action'] = 'add';
334+
} else if ( $t_file_matches[3] == 'deleted' or $t_file_matches[3] == 'similarity' ) {
335+
$t_file['action'] = 'rm';
336+
} else {
337+
$t_file['action'] = 'mod';
338+
}
339+
} else {
340+
$t_file['action'] = 'mod';
341+
}
342+
343+
$t_commit['files'][] = $t_file;
344+
}
345+
346+
$t_changeset = new SourceChangeset( $p_repo->id, $t_commit['revision'], $p_branch,
347+
$t_commit['date'], $t_commit['author'], $t_commit['message'], 0,
348+
( isset( $t_commit['parent'] ) ? $t_commit['parent'] : '' ) );
349+
350+
$t_changeset->author_email = $t_commit['author_email'];
351+
$t_changeset->committer = $t_commit['committer'];
352+
$t_changeset->committer_email = $t_commit['committer_email'];
353+
354+
foreach( $t_commit['files'] as $t_file ) {
355+
$t_changeset->files[] = new SourceFile( 0, $t_file['revision'], $t_file['filename'], $t_file['action'] );
356+
}
357+
358+
$t_changeset->save();
359+
360+
echo "saved.\n";
361+
return array( $t_changeset, $t_parents );
362+
} else {
363+
echo "already exists.\n";
364+
return array( null, array() );
365+
}
366+
}
367+
}

0 commit comments

Comments
 (0)