3
final class ArcanistBaseCommitParser {
7
private $verbose = false;
9
public function __construct(ArcanistRepositoryAPI $api) {
14
private function tokenizeBaseCommitSpecification($raw_spec) {
19
$spec = preg_split('/\s*,\s*/', $raw_spec);
20
$spec = array_filter($spec);
22
foreach ($spec as $rule) {
23
if (strpos($rule, ':') === false) {
24
throw new ArcanistUsageException(
25
"Rule '{$rule}' is invalid, it must have a type and name like ".
33
private function log($message) {
35
fwrite(STDERR, $message."\n");
39
public function resolveBaseCommit(array $specs) {
48
foreach ($specs as $source => $spec) {
49
$specs[$source] = self::tokenizeBaseCommitSpecification($spec);
61
$source = head($this->try);
63
if (!idx($specs, $source)) {
64
$this->log("No rules left from source '{$source}'.");
65
array_shift($this->try);
69
$this->log("Trying rules from source '{$source}'.");
71
$rules = &$specs[$source];
72
while ($rule = array_shift($rules)) {
73
$this->log("Trying rule '{$rule}'.");
75
$commit = $this->resolveRule($rule, $source);
77
if ($commit === false) {
78
// If a rule returns false, it means to go to the next ruleset.
80
} else if ($commit !== null) {
81
$this->log("Resolved commit '{$commit}' from rule '{$rule}'.");
91
* Handle resolving individual rules.
93
private function resolveRule($rule, $source) {
94
// NOTE: Returning `null` from this method means "no match".
95
// Returning `false` from this method means "stop current ruleset".
97
list($type, $name) = explode(':', $rule, 2);
103
return $this->api->resolveBaseCommitRule($rule, $source);
105
return $this->resolveArcRule($rule, $name, $source);
107
throw new ArcanistUsageException(
108
"Base commit rule '{$rule}' (from source '{$source}') ".
109
"is not a recognized rule.");
115
* Handle resolving "arc:*" rules.
117
private function resolveArcRule($rule, $name, $source) {
118
$name = $this->updateLegacyRuleName($name);
122
$this->verbose = true;
123
$this->log('Enabled verbose mode.');
126
$reason = 'it is what you typed when prompted.';
127
$this->api->setBaseCommitExplanation($reason);
128
return phutil_console_prompt('Against which commit?');
134
// Push the other source on top of the list.
135
array_unshift($this->try, $name);
136
$this->log("Switching to source '{$name}'.");
139
// Cycle this source to the end of the list.
140
$this->try[] = array_shift($this->try);
141
$this->log("Yielding processing of rules from '{$source}'.");
144
// Dump the whole stack.
145
$this->try = array();
146
$this->log('Halting all rule processing.');
156
return $this->api->resolveBaseCommitRule($rule, $source);
159
if (preg_match('/^exec\((.*)\)$/', $name, $matches)) {
160
$root = $this->api->getWorkingCopyIdentity()->getProjectRoot();
161
$future = new ExecFuture('%C', $matches[1]);
162
$future->setCWD($root);
163
list($err, $stdout) = $future->resolve();
165
return trim($stdout);
169
} else if (preg_match('/^nodiff\((.*)\)$/', $name, $matches)) {
170
return $this->api->resolveBaseCommitRule($rule, $source);
173
throw new ArcanistUsageException(
174
"Base commit rule '{$rule}' (from source '{$source}') ".
175
"is not a recognized rule.");
179
private function updateLegacyRuleName($name) {
184
$new_name = idx($updated, $name);
186
$this->log("translating legacy name '$name' to '$new_name'");