Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F81129683
DiffusionCachedResolveRefsQuery.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Wed, Sep 4, 08:34
Size
5 KB
Mime Type
text/x-php
Expires
Fri, Sep 6, 08:34 (2 d)
Engine
blob
Format
Raw Data
Handle
20521077
Attached To
rPH Phabricator
DiffusionCachedResolveRefsQuery.php
View Options
<?php
/**
* Resolves references into canonical, stable commit identifiers by examining
* database caches.
*
* This is a counterpart to @{class:DiffusionLowLevelResolveRefsQuery}. This
* query offers fast resolution, but can not resolve everything that the
* low-level query can.
*
* This class can resolve the most common refs (commits, branches, tags) and
* can do so cheapy (by examining the database, without needing to make calls
* to the VCS or the service host).
*/
final
class
DiffusionCachedResolveRefsQuery
extends
DiffusionLowLevelQuery
{
private
$refs
;
private
$types
;
public
function
withRefs
(
array
$refs
)
{
$this
->
refs
=
$refs
;
return
$this
;
}
public
function
withTypes
(
array
$types
)
{
$this
->
types
=
$types
;
return
$this
;
}
protected
function
executeQuery
()
{
if
(!
$this
->
refs
)
{
return
array
();
}
switch
(
$this
->
getRepository
()->
getVersionControlSystem
())
{
case
PhabricatorRepositoryType
::
REPOSITORY_TYPE_GIT
:
case
PhabricatorRepositoryType
::
REPOSITORY_TYPE_MERCURIAL
:
$result
=
$this
->
resolveGitAndMercurialRefs
();
break
;
case
PhabricatorRepositoryType
::
REPOSITORY_TYPE_SVN
:
$result
=
$this
->
resolveSubversionRefs
();
break
;
default
:
throw
new
Exception
(
pht
(
'Unsupported repository type!'
));
}
if
(
$this
->
types
!==
null
)
{
$result
=
$this
->
filterRefsByType
(
$result
,
$this
->
types
);
}
return
$result
;
}
/**
* Resolve refs in Git and Mercurial repositories.
*
* We can resolve commit hashes from the commits table, and branch and tag
* names from the refcursor table.
*/
private
function
resolveGitAndMercurialRefs
()
{
$repository
=
$this
->
getRepository
();
$conn_r
=
$repository
->
establishConnection
(
'r'
);
$results
=
array
();
$prefixes
=
array
();
foreach
(
$this
->
refs
as
$ref
)
{
// We require refs to look like hashes and be at least 4 characters
// long. This is similar to the behavior of git.
if
(
preg_match
(
'/^[a-f0-9]{4,}$/'
,
$ref
))
{
$prefixes
[]
=
qsprintf
(
$conn_r
,
'(commitIdentifier LIKE %>)'
,
$ref
);
}
}
if
(
$prefixes
)
{
$commits
=
queryfx_all
(
$conn_r
,
'SELECT commitIdentifier FROM %T
WHERE repositoryID = %s AND %Q'
,
id
(
new
PhabricatorRepositoryCommit
())->
getTableName
(),
$repository
->
getID
(),
implode
(
' OR '
,
$prefixes
));
foreach
(
$commits
as
$commit
)
{
$hash
=
$commit
[
'commitIdentifier'
];
foreach
(
$this
->
refs
as
$ref
)
{
if
(!
strncmp
(
$hash
,
$ref
,
strlen
(
$ref
)))
{
$results
[
$ref
][]
=
array
(
'type'
=>
'commit'
,
'identifier'
=>
$hash
,
);
}
}
}
}
$name_hashes
=
array
();
foreach
(
$this
->
refs
as
$ref
)
{
$name_hashes
[
PhabricatorHash
::
digestForIndex
(
$ref
)]
=
$ref
;
}
$cursors
=
queryfx_all
(
$conn_r
,
'SELECT refNameHash, refType, commitIdentifier, isClosed FROM %T
WHERE repositoryPHID = %s AND refNameHash IN (%Ls)'
,
id
(
new
PhabricatorRepositoryRefCursor
())->
getTableName
(),
$repository
->
getPHID
(),
array_keys
(
$name_hashes
));
foreach
(
$cursors
as
$cursor
)
{
if
(
isset
(
$name_hashes
[
$cursor
[
'refNameHash'
]]))
{
$results
[
$name_hashes
[
$cursor
[
'refNameHash'
]]][]
=
array
(
'type'
=>
$cursor
[
'refType'
],
'identifier'
=>
$cursor
[
'commitIdentifier'
],
'closed'
=>
(
bool
)
$cursor
[
'isClosed'
],
);
// TODO: In Git, we don't store (and thus don't return) the hash
// of the tag itself. It would be vaguely nice to do this.
}
}
return
$results
;
}
/**
* Resolve refs in Subversion repositories.
*
* We can resolve all numeric identifiers and the keyword `HEAD`.
*/
private
function
resolveSubversionRefs
()
{
$repository
=
$this
->
getRepository
();
$max_commit
=
id
(
new
PhabricatorRepositoryCommit
())
->
loadOneWhere
(
'repositoryID = %d ORDER BY epoch DESC, id DESC LIMIT 1'
,
$repository
->
getID
());
if
(!
$max_commit
)
{
// This repository is empty or hasn't parsed yet, so none of the refs are
// going to resolve.
return
array
();
}
$max_commit_id
=
(
int
)
$max_commit
->
getCommitIdentifier
();
$results
=
array
();
foreach
(
$this
->
refs
as
$ref
)
{
if
(
$ref
==
'HEAD'
)
{
// Resolve "HEAD" to mean "the most recent commit".
$results
[
$ref
][]
=
array
(
'type'
=>
'commit'
,
'identifier'
=>
$max_commit_id
,
);
continue
;
}
if
(!
preg_match
(
'/^
\d
+$/'
,
$ref
))
{
// This ref is non-numeric, so it doesn't resolve to anything.
continue
;
}
// Resolve other commits if we can deduce their existence.
// TODO: When we import only part of a repository, we won't necessarily
// have all of the smaller commits. Should we fail to resolve them here
// for repositories with a subpath? It might let us simplify other things
// elsewhere.
if
((
int
)
$ref
<=
$max_commit_id
)
{
$results
[
$ref
][]
=
array
(
'type'
=>
'commit'
,
'identifier'
=>
(
int
)
$ref
,
);
}
}
return
$results
;
}
}
Event Timeline
Log In to Comment