<?php
class
IDF_Scm_Mercurial
extends
IDF_Scm
{
public
function
__construct(
$repo
,
$project
=null)
{
$this
->repo =
$repo
;
$this
->project =
$project
;
}
public
function
getRepositorySize()
{
$cmd
= Pluf::f(
'idf_exec_cmd_prefix'
,
''
).
'du -sk '
.
escapeshellarg
(
$this
->repo);
$out
=
explode
(
' '
,
self::shell_exec(
'IDF_Scm_Mercurial::getRepositorySize'
,
$cmd
),
2);
return
(int)
$out
[0]*1024;
}
public
static
function
factory(
$project
)
{
$rep
= sprintf(Pluf::f(
'mercurial_repositories'
),
$project
->shortname);
return
new
IDF_Scm_Mercurial(
$rep
,
$project
);
}
public
function
isAvailable()
{
try
{
$branches
=
$this
->getBranches();
}
catch
(IDF_Scm_Exception
$e
) {
return
false;
}
return
(
count
(
$branches
) > 0);
}
public
function
findAuthor(
$author
)
{
$match
=
array
();
if
(!preg_match(
'/<(.*)>/'
,
$author
,
$match
)) {
return
null;
}
return
Pluf::factory(
'IDF_EmailAddress'
)->get_user_for_email_address(
$match
[1]);
}
public
function
getMainBranch()
{
return
'tip'
;
}
public
static
function
getAnonymousAccessUrl(
$project
,
$commit
=null)
{
return
sprintf(Pluf::f(
'mercurial_remote_url'
),
$project
->shortname);
}
public
static
function
getAuthAccessUrl(
$project
,
$user
,
$commit
=null)
{
return
sprintf(Pluf::f(
'mercurial_remote_url'
),
$project
->shortname);
}
public
function
validateRevision(
$rev
)
{
$cmd
= sprintf(Pluf::f(
'hg_path'
,
'hg'
).
' log -R %s -r %s'
,
escapeshellarg
(
$this
->repo),
escapeshellarg
(
$rev
));
$cmd
= Pluf::f(
'idf_exec_cmd_prefix'
,
''
).
$cmd
;
self::
exec
(
'IDF_Scm_Mercurial::isValidRevision'
,
$cmd
,
$out
,
$ret
);
if
(
$ret
== 0 &&
count
(
$out
) > 0)
return
IDF_Scm::REVISION_VALID;
return
IDF_Scm::REVISION_INVALID;
}
public
function
testHash(
$hash
,
$dummy
=null)
{
$cmd
= sprintf(Pluf::f(
'hg_path'
,
'hg'
).
' log -R %s -r %s'
,
escapeshellarg
(
$this
->repo),
escapeshellarg
(
$hash
));
$ret
= 0;
$out
=
array
();
$cmd
= Pluf::f(
'idf_exec_cmd_prefix'
,
''
).
$cmd
;
self::
exec
(
'IDF_Scm_Mercurial::testHash'
,
$cmd
,
$out
,
$ret
);
return
(
$ret
!= 0) ? false :
'commit'
;
}
public
function
getTree(
$commit
,
$folder
=
'/'
,
$branch
=null)
{
$folder
= (
$folder
==
'/'
) ?
''
:
$folder
;
$co
=
$this
->getCommit(
$commit
);
if
(
$folder
) {
$found
= false;
foreach
(
$this
->getTreeInfo(
$co
->tree, true,
''
, true)
as
$file
) {
if
(
$file
->type ==
'tree'
and
$file
->file ==
$folder
) {
$found
= true;
break
;
}
}
if
(!
$found
) {
throw
new
Exception(sprintf(__(
'Folder %1$s not found in commit %2$s.'
),
$folder
,
$commit
));
}
}
$res
=
$this
->getTreeInfo(
$commit
,
$recurse
=true,
$folder
);
return
$res
;
}
public
function
getTreeInfo(
$tree
,
$recurse
=true,
$folder
=
''
,
$root
=false)
{
if
(
'commit'
!=
$this
->testHash(
$tree
)) {
throw
new
Exception(sprintf(__(
'Not a valid tree: %s.'
),
$tree
));
}
$cmd_tmpl
= Pluf::f(
'hg_path'
,
'hg'
).
' manifest -R %s --debug -r %s'
;
$cmd
= sprintf(
$cmd_tmpl
,
escapeshellarg
(
$this
->repo),
$tree
, (
$recurse
) ?
''
:
''
);
$out
=
array
();
$res
=
array
();
$cmd
= Pluf::f(
'idf_exec_cmd_prefix'
,
''
).
$cmd
;
self::
exec
(
'IDF_Scm_Mercurial::getTreeInfo'
,
$cmd
,
$out
);
$tmp_hack
=
array
();
while
(null !== (
$line
=
array_pop
(
$out
))) {
list(
$hash
,
$perm
,
$exec
,
$file
) = preg_split(
'/ |\t/'
,
$line
, 4);
$file
= trim(
$file
);
$dir
=
explode
(
'/'
,
$file
, -1);
$tmp
=
''
;
for
(
$i
=0,
$n
=
count
(
$dir
);
$i
<
$n
;
$i
++) {
if
(
$i
> 0) {
$tmp
.=
'/'
;
}
$tmp
.=
$dir
[
$i
];
if
(!isset(
$tmp_hack
[
"empty\t000\t\t$tmp/"
])) {
$out
[] =
"empty\t000\t\t$tmp/"
;
$tmp_hack
[
"empty\t000\t\t$tmp/"
] = 1;
}
}
if
(preg_match(
'/^(.*)\/$/'
,
$file
,
$match
)) {
$type
=
'tree'
;
$file
=
$match
[1];
}
else
{
$type
=
'blob'
;
}
if
(!
$root
and
!
$folder
and
preg_match(
'/^.*\/.*$/'
,
$file
)) {
continue
;
}
if
(
$folder
) {
preg_match(
'|^'
.
$folder
.
'[/]?([^/]+)?$|'
,
$file
,
$match
);
if
(
count
(
$match
) > 1) {
$file
=
$match
[1];
}
else
{
continue
;
}
}
$fullpath
= (
$folder
) ?
$folder
.
'/'
.
$file
:
$file
;
$efullpath
= self::smartEncode(
$fullpath
);
$res
[] = (object)
array
(
'perm'
=>
$perm
,
'type'
=>
$type
,
'hash'
=>
$hash
,
'fullpath'
=>
$fullpath
,
'efullpath'
=>
$efullpath
,
'file'
=>
$file
);
}
return
$res
;
}
public
function
getPathInfo(
$totest
,
$commit
=
'tip'
)
{
$cmd_tmpl
= Pluf::f(
'hg_path'
,
'hg'
).
' manifest -R %s --debug -r %s'
;
$cmd
= sprintf(
$cmd_tmpl
,
escapeshellarg
(
$this
->repo),
$commit
);
$out
=
array
();
$cmd
= Pluf::f(
'idf_exec_cmd_prefix'
,
''
).
$cmd
;
self::
exec
(
'IDF_Scm_Mercurial::getPathInfo'
,
$cmd
,
$out
);
$tmp_hack
=
array
();
while
(null !== (
$line
=
array_pop
(
$out
))) {
list(
$hash
,
$perm
,
$exec
,
$file
) = preg_split(
'/ |\t/'
,
$line
, 4);
$file
= trim(
$file
);
$dir
=
explode
(
'/'
,
$file
, -1);
$tmp
=
''
;
for
(
$i
=0,
$n
=
count
(
$dir
);
$i
<
$n
;
$i
++) {
if
(
$i
> 0) {
$tmp
.=
'/'
;
}
$tmp
.=
$dir
[
$i
];
if
(
$tmp
==
$totest
) {
$pathinfo
=
pathinfo
(
$totest
);
return
(object)
array
(
'perm'
=>
'000'
,
'type'
=>
'tree'
,
'hash'
=>
$hash
,
'fullpath'
=>
$totest
,
'file'
=>
$pathinfo
[
'basename'
],
'commit'
=>
$commit
);
}
if
(!isset(
$tmp_hack
[
"empty\t000\t\t$tmp/"
])) {
$out
[] =
"empty\t000\t\t$tmp/"
;
$tmp_hack
[
"empty\t000\t\t$tmp/"
] = 1;
}
}
if
(preg_match(
'/^(.*)\/$/'
,
$file
,
$match
)) {
$type
=
'tree'
;
$file
=
$match
[1];
}
else
{
$type
=
'blob'
;
}
if
(
$totest
==
$file
) {
$pathinfo
=
pathinfo
(
$totest
);
return
(object)
array
(
'perm'
=>
$perm
,
'type'
=>
$type
,
'hash'
=>
$hash
,
'fullpath'
=>
$totest
,
'file'
=>
$pathinfo
[
'basename'
],
'commit'
=>
$commit
);
}
}
return
false;
}
public
function
getFile(
$def
,
$cmd_only
=false)
{
$cmd
= sprintf(Pluf::f(
'hg_path'
,
'hg'
).
' cat -R %s -r %s %s'
,
escapeshellarg
(
$this
->repo),
escapeshellarg
(
$def
->commit),
escapeshellarg
(
$this
->repo.
'/'
.
$def
->fullpath));
$cmd
= Pluf::f(
'idf_exec_cmd_prefix'
,
''
).
$cmd
;
return
(
$cmd_only
) ?
$cmd
: self::shell_exec(
'IDF_Scm_Mercurial::getFile'
,
$cmd
);
}
public
function
getBranches()
{
if
(isset(
$this
->cache[
'branches'
])) {
return
$this
->cache[
'branches'
];
}
$out
=
array
();
$cmd
= sprintf(Pluf::f(
'hg_path'
,
'hg'
).
' branches -R %s'
,
escapeshellarg
(
$this
->repo));
$cmd
= Pluf::f(
'idf_exec_cmd_prefix'
,
''
).
$cmd
;
self::
exec
(
'IDF_Scm_Mercurial::getBranches'
,
$cmd
,
$out
);
$res
=
array
();
foreach
(
$out
as
$b
) {
preg_match(
'/(\S+).*\S+:(\S+)/'
,
$b
,
$match
);
$res
[
$match
[1]] =
''
;
}
$this
->cache[
'branches'
] =
$res
;
return
$res
;
}
public
function
getTags()
{
if
(isset(
$this
->cache[
'tags'
])) {
return
$this
->cache[
'tags'
];
}
$out
=
array
();
$cmd
= sprintf(Pluf::f(
'hg_path'
,
'hg'
).
' tags -R %s'
,
escapeshellarg
(
$this
->repo));
$cmd
= Pluf::f(
'idf_exec_cmd_prefix'
,
''
).
$cmd
;
self::
exec
(
'IDF_Scm_Mercurial::getTags'
,
$cmd
,
$out
);
$res
=
array
();
foreach
(
$out
as
$b
) {
preg_match(
'/(\S+).*\S+:(\S+)/'
,
$b
,
$match
);
$res
[
$match
[1]] =
''
;
}
$this
->cache[
'tags'
] =
$res
;
return
$res
;
}
public
function
inBranches(
$commit
,
$path
)
{
return
(in_array(
$commit
,
array_keys
(
$this
->getBranches())))
?
array
(
$commit
) :
array
();
}
public
function
inTags(
$commit
,
$path
)
{
return
(in_array(
$commit
,
array_keys
(
$this
->getTags())))
?
array
(
$commit
) :
array
();
}
public
function
getCommit(
$commit
,
$getdiff
=false)
{
if
(!
$this
->isValidRevision(
$commit
)) {
return
false;
}
$tmpl
= (
$getdiff
) ?
Pluf::f(
'hg_path'
,
'hg'
).
' log -p -r %s -R %s'
: Pluf::f(
'hg_path'
,
'hg'
).
' log -r %s -R %s'
;
$cmd
= sprintf(
$tmpl
,
escapeshellarg
(
$commit
),
escapeshellarg
(
$this
->repo));
$out
=
array
();
$cmd
= Pluf::f(
'idf_exec_cmd_prefix'
,
''
).
$cmd
;
self::
exec
(
'IDF_Scm_Mercurial::getCommit'
,
$cmd
,
$out
);
$log
=
array
();
$change
=
array
();
$inchange
= false;
foreach
(
$out
as
$line
) {
if
(!
$inchange
and
0 ===
strpos
(
$line
,
'diff -r'
)) {
$inchange
= true;
}
if
(
$inchange
) {
$change
[] =
$line
;
}
else
{
$log
[] =
$line
;
}
}
$out
= self::parseLog(
$log
, 6);
$out
[0]->diff = implode(
"\n"
,
$change
);
return
$out
[0];
}
public
function
isCommitLarge(
$commit
=
'HEAD'
)
{
return
false;
}
public
function
getChangeLog(
$commit
=
'tip'
,
$n
=10)
{
$cmd
= sprintf(Pluf::f(
'hg_path'
,
'hg'
).
' log -R %s -l%s '
,
escapeshellarg
(
$this
->repo),
$n
,
$commit
);
$out
=
array
();
$cmd
= Pluf::f(
'idf_exec_cmd_prefix'
,
''
).
$cmd
;
self::
exec
(
'IDF_Scm_Mercurial::getChangeLog'
,
$cmd
,
$out
);
return
self::parseLog(
$out
, 6);
}
public
static
function
parseLog(
$lines
,
$hdrs
=3)
{
$res
=
array
();
$c
=
array
();
$i
= 0;
$hdrs
+= 1;
foreach
(
$lines
as
$line
) {
$i
++;
if
(0 ===
strpos
(
$line
,
'changeset:'
)) {
if
(
count
(
$c
) > 0) {
$c
[
'full_message'
] = trim(
$c
[
'full_message'
]);
$res
[] = (object)
$c
;
}
$c
=
array
();
$c
[
'commit'
] =
substr
(
strrchr
(
$line
,
':'
), 1);
$c
[
'full_message'
] =
''
;
$i
=1;
continue
;
}
if
(
$i
==
$hdrs
) {
$c
[
'title'
] = trim(
$line
);
continue
;
}
$match
=
array
();
if
(preg_match(
'/(\S+)\s*:\s*(.*)/'
,
$line
,
$match
)) {
$match
[1] =
strtolower
(
$match
[1]);
if
(
$match
[1] ==
'user'
) {
$c
[
'author'
] =
$match
[2];
}
elseif
(
$match
[1] ==
'summary'
) {
$c
[
'title'
] =
$match
[2];
}
elseif
(
$match
[1] ==
'branch'
) {
$c
[
'branch'
] =
$match
[2];
}
else
{
$c
[
$match
[1]] = trim(
$match
[2]);
}
if
(
$match
[1] ==
'date'
) {
$c
[
'date'
] =
gmdate
(
'Y-m-d H:i:s'
,
strtotime
(
$match
[2]));
}
continue
;
}
if
(
$i
> (
$hdrs
+1)) {
$c
[
'full_message'
] .= trim(
$line
).
"\n"
;
continue
;
}
}
$c
[
'tree'
] = !
empty
(
$c
[
'commit'
]) ? trim(
$c
[
'commit'
]) :
''
;
$c
[
'branch'
] =
empty
(
$c
[
'branch'
]) ?
'default'
:
$c
[
'branch'
];
$c
[
'full_message'
] = !
empty
(
$c
[
'full_message'
]) ? trim(
$c
[
'full_message'
]) :
''
;
$res
[] = (object)
$c
;
return
$res
;
}
protected
function
getArchiveStream(
$commit
,
$prefix
=
''
)
{
$cmd
= sprintf(Pluf::f(
'idf_exec_cmd_prefix'
,
''
).
Pluf::f(
'hg_path'
,
'hg'
).
' archive --type=zip -R %s -r %s -'
,
escapeshellarg
(
$this
->repo),
escapeshellarg
(
$commit
));
return
new
Pluf_HTTP_Response_CommandPassThru(
$cmd
,
'application/x-zip'
);
}
}