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

View file

@ -1590,11 +1590,15 @@ EOTEXT
return $this->getGitUpdateMessage(); return $this->getGitUpdateMessage();
} }
if ($repository_api instanceof ArcanistMercurialAPI) {
return $this->getMercurialUpdateMessage();
}
return null; 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 * @task message
*/ */
@ -1617,14 +1621,7 @@ EOTEXT
// we always get this 100% right, we're just trying to do something // we always get this 100% right, we're just trying to do something
// reasonable. // reasonable.
$current_diff = $this->getConduit()->callMethodSynchronous( $local = $this->loadActiveLocalCommitInfo();
'differential.getdiff',
array(
'revision_id' => $this->revisionID,
));
$properties = idx($current_diff, 'properties', array());
$local = idx($properties, 'local:commits', array());
$hashes = ipull($local, null, 'commit'); $hashes = ipull($local, null, 'commit');
$usable = array(); $usable = array();
@ -1653,6 +1650,49 @@ EOTEXT
return null; 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 // Flip messages so they'll read chronologically (oldest-first) in the
// template, e.g.: // template, e.g.:
// //
@ -1664,7 +1704,7 @@ EOTEXT
$default = array(); $default = array();
foreach ($usable as $message) { foreach ($usable as $message) {
// Pick the first line out of each message. // Pick the first line out of each message.
$text = trim($message->getMetadata('message')); $text = trim($message);
$text = head(explode("\n", $text)); $text = head(explode("\n", $text));
$default[] = ' - '.$text."\n"; $default[] = ' - '.$text."\n";
} }
@ -1672,6 +1712,17 @@ EOTEXT
return implode('', $default); 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 )------------------------------------------------- */ /* -( Diff Specification )------------------------------------------------- */