1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-22 06:42:41 +01:00

Prefill template update message in Mercurial during --update

Summary:
When a user invokes the update flow from Mercurial, prefill the update message
like we do for Git.

Also add a little caching to improve performance since "hg" execs cost ~100ms.

Test Plan: Updated a Mercurial diff repeatedly, got sensible default message
fills.

Reviewers: Makinde, btrahan

Reviewed By: Makinde

CC: aran, epriestley

Maniphest Tasks: T28

Differential Revision: https://secure.phabricator.com/D1718
This commit is contained in:
epriestley 2012-02-28 16:59:40 -08:00
parent bb4616cd7c
commit 1f13e022cd
2 changed files with 126 additions and 59 deletions

View file

@ -26,6 +26,8 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
private $status;
private $base;
private $relativeCommit;
private $workingCopyRevision;
private $localCommitInfo;
public function execxLocal($pattern /*, ... */) {
$args = func_get_args();
@ -140,37 +142,40 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
}
public function getLocalCommitInformation() {
list($info) = $this->execxLocal(
'log --style default --rev %s..%s --',
$this->getRelativeCommit(),
$this->getWorkingCopyRevision());
$logs = ArcanistMercurialParser::parseMercurialLog($info);
if ($this->localCommitInfo === null) {
list($info) = $this->execxLocal(
'log --style default --rev %s..%s --',
$this->getRelativeCommit(),
$this->getWorkingCopyRevision());
$logs = ArcanistMercurialParser::parseMercurialLog($info);
// Get rid of the first log, it's not actually part of the diff. "hg log"
// is inclusive, while "hg diff" is exclusive.
array_shift($logs);
// Get rid of the first log, it's not actually part of the diff. "hg log"
// is inclusive, while "hg diff" is exclusive.
array_shift($logs);
// Expand short hashes (12 characters) to full hashes (40 characters) by
// issuing a big "hg log" command. Possibly we should do this with parents
// too, but nothing uses them directly at the moment.
if ($logs) {
$cmd = array();
foreach (ipull($logs, 'rev') as $rev) {
$cmd[] = csprintf('--rev %s', $rev);
}
list($full) = $this->execxLocal(
'log --template %s %C --',
'{node}\\n',
implode(' ', $cmd));
$full = explode("\n", trim($full));
foreach ($logs as $key => $dict) {
$logs[$key]['rev'] = array_pop($full);
// Expand short hashes (12 characters) to full hashes (40 characters) by
// issuing a big "hg log" command. Possibly we should do this with parents
// too, but nothing uses them directly at the moment.
if ($logs) {
$cmd = array();
foreach (ipull($logs, 'rev') as $rev) {
$cmd[] = csprintf('--rev %s', $rev);
}
list($full) = $this->execxLocal(
'log --template %s %C --',
'{node}\\n',
implode(' ', $cmd));
$full = explode("\n", trim($full));
foreach ($logs as $key => $dict) {
$logs[$key]['rev'] = array_pop($full);
}
}
$this->localCommitInfo = $logs;
}
return $logs;
return $this->localCommitInfo;
}
public function getBlame($path) {
@ -318,20 +323,25 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
}
public function getWorkingCopyRevision() {
// In Mercurial, "tip" means the tip of the current branch, not what's in
// the working copy. The tip may be ahead of the working copy. We need to
// use "hg summary" to figure out what is actually in the working copy.
// For instance, "hg up 4 && arc diff" should not show commits 5 and above.
if ($this->workingCopyRevision === null) {
// In Mercurial, "tip" means the tip of the current branch, not what's in
// the working copy. The tip may be ahead of the working copy. We need to
// use "hg summary" to figure out what is actually in the working copy.
// For instance, "hg up 4 && arc diff" should not show commits 5 and
// above.
// Without arguments, "hg id" shows the current working directory's commit,
// and "--debug" expands it to a 40-character hash.
list($stdout) = $this->execxLocal('--debug id --id');
// Without arguments, "hg id" shows the current working directory's
// commit, and "--debug" expands it to a 40-character hash.
list($stdout) = $this->execxLocal('--debug id --id');
// Even with "--id", "hg id" will print a trailing "+" after the hash
// if the working copy is dirty (has uncommitted changes). We'll explicitly
// detect this later by calling getWorkingCopyStatus(); ignore it for now.
$stdout = trim($stdout);
return rtrim($stdout, '+');
// Even with "--id", "hg id" will print a trailing "+" after the hash
// if the working copy is dirty (has uncommitted changes). We'll
// explicitly detect this later by calling getWorkingCopyStatus(); ignore
// it for now.
$stdout = trim($stdout);
$this->workingCopyRevision = rtrim($stdout, '+');
}
return $this->workingCopyRevision;
}
public function supportsRelativeLocalCommits() {
@ -384,6 +394,23 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
"'hg push' or by printing and faxing it).";
}
public function getCommitMessageLog() {
list($stdout) = $this->execxLocal(
"log --template '{node}\\1{desc}\\0' --rev %s..%s --",
$this->getRelativeCommit(),
$this->getWorkingCopyRevision());
$map = array();
$logs = explode("\0", trim($stdout));
foreach (array_filter($logs) as $log) {
list($node, $desc) = explode("\1", $log);
$map[$node] = $desc;
}
return array_reverse($map);
}
public function loadWorkingCopyDifferentialRevisions(
ConduitClient $conduit,
array $query) {
@ -400,17 +427,6 @@ final class ArcanistMercurialAPI extends ArcanistRepositoryAPI {
'commitHashes' => $hashes,
));
if ($results) {
return $results;
}
// If we still didn't succeed, try to find revisions by branch name.
$results = $conduit->callMethodSynchronous(
'differential.query',
$query + array(
'branches' => array($this->getBranchName()),
));
return $results;
}

View file

@ -1590,11 +1590,15 @@ EOTEXT
return $this->getGitUpdateMessage();
}
if ($repository_api instanceof ArcanistMercurialAPI) {
return $this->getMercurialUpdateMessage();
}
return null;
}
/**
* Retrieve the git message in HEAD if it isn't a primary template message.
* Retrieve the git messages between HEAD and the last update.
*
* @task message
*/
@ -1617,14 +1621,7 @@ EOTEXT
// we always get this 100% right, we're just trying to do something
// reasonable.
$current_diff = $this->getConduit()->callMethodSynchronous(
'differential.getdiff',
array(
'revision_id' => $this->revisionID,
));
$properties = idx($current_diff, 'properties', array());
$local = idx($properties, 'local:commits', array());
$local = $this->loadActiveLocalCommitInfo();
$hashes = ipull($local, null, 'commit');
$usable = array();
@ -1653,6 +1650,49 @@ EOTEXT
return null;
}
return $this->formatUsableLogs($usable);
}
/**
* Retrieve the hg messages between tip and the last update.
*
* @task message
*/
private function getMercurialUpdateMessage() {
$repository_api = $this->getRepositoryAPI();
$messages = $repository_api->getCommitMessageLog();
$local = $this->loadActiveLocalCommitInfo();
$hashes = ipull($local, null, 'rev');
$usable = array();
foreach ($messages as $rev => $message) {
if (isset($hashes[$rev])) {
// If this commit is currently part of the active diff on the revision,
// stop using commit messages, since anything older than this isn't new.
break;
}
// Otherwise, this looks new, so it's a usable commit message.
$usable[] = $message;
}
if (!$usable) {
// No new commit messages, so we don't have anywhere to start from.
return null;
}
return $this->formatUsableLogs($usable);
}
/**
* Format log messages to prefill a diff update.
*
* @task message
*/
private function formatUsableLogs(array $usable) {
// Flip messages so they'll read chronologically (oldest-first) in the
// template, e.g.:
//
@ -1664,7 +1704,7 @@ EOTEXT
$default = array();
foreach ($usable as $message) {
// Pick the first line out of each message.
$text = trim($message->getMetadata('message'));
$text = trim($message);
$text = head(explode("\n", $text));
$default[] = ' - '.$text."\n";
}
@ -1672,6 +1712,17 @@ EOTEXT
return implode('', $default);
}
private function loadActiveLocalCommitInfo() {
$current_diff = $this->getConduit()->callMethodSynchronous(
'differential.getdiff',
array(
'revision_id' => $this->revisionID,
));
$properties = idx($current_diff, 'properties', array());
return idx($properties, 'local:commits', array());
}
/* -( Diff Specification )------------------------------------------------- */