in src/repository/api/ArcanistGitAPI.php [1339:1505]
public function resolveBaseCommitRule($rule, $source) {
list($type, $name) = explode(':', $rule, 2);
switch ($type) {
case 'git':
$matches = null;
if (preg_match('/^merge-base\((.+)\)$/', $name, $matches)) {
list($err, $merge_base) = $this->execManualLocal(
'merge-base %s HEAD',
$matches[1]);
if (!$err) {
$this->setBaseCommitExplanation(
pht(
"it is the merge-base of '%s' and HEAD, as specified by ".
"'%s' in your %s 'base' configuration.",
$matches[1],
$rule,
$source));
return trim($merge_base);
}
} else if (preg_match('/^branch-unique\((.+)\)$/', $name, $matches)) {
list($err, $merge_base) = $this->execManualLocal(
'merge-base %s HEAD',
$matches[1]);
if ($err) {
return null;
}
$merge_base = trim($merge_base);
list($commits) = $this->execxLocal(
'log --format=%C %s..HEAD --',
'%H',
$merge_base);
$commits = array_filter(explode("\n", $commits));
if (!$commits) {
return null;
}
$commits[] = $merge_base;
$head_branch_count = null;
$all_branch_names = ipull($this->getAllBranches(), 'name');
foreach ($commits as $commit) {
// Ideally, we would use something like "for-each-ref --contains"
// to get a filtered list of branches ready for script consumption.
// Instead, try to get predictable output from "branch --contains".
$flags = array();
$flags[] = '--no-color';
// NOTE: The "--no-column" flag was introduced in Git 1.7.11, so
// don't pass it if we're running an older version. See T9953.
$version = $this->getGitVersion();
if (version_compare($version, '1.7.11', '>=')) {
$flags[] = '--no-column';
}
list($branches) = $this->execxLocal(
'branch %Ls --contains %s',
$flags,
$commit);
$branches = array_filter(explode("\n", $branches));
// Filter the list, removing the "current" marker (*) and ignoring
// anything other than known branch names (mainly, any possible
// "detached HEAD" or "no branch" line).
foreach ($branches as $key => $branch) {
$branch = trim($branch, ' *');
if (in_array($branch, $all_branch_names)) {
$branches[$key] = $branch;
} else {
unset($branches[$key]);
}
}
if ($head_branch_count === null) {
// If this is the first commit, it's HEAD. Count how many
// branches it is on; we want to include commits on the same
// number of branches. This covers a case where this branch
// has sub-branches and we're running "arc diff" here again
// for whatever reason.
$head_branch_count = count($branches);
} else if (count($branches) > $head_branch_count) {
$branches = implode(', ', $branches);
$this->setBaseCommitExplanation(
pht(
"it is the first commit between '%s' (the merge-base of ".
"'%s' and HEAD) which is also contained by another branch ".
"(%s).",
$merge_base,
$matches[1],
$branches));
return $commit;
}
}
} else {
list($err) = $this->execManualLocal(
'cat-file -t %s',
$name);
if (!$err) {
$this->setBaseCommitExplanation(
pht(
"it is specified by '%s' in your %s 'base' configuration.",
$rule,
$source));
return $name;
}
}
break;
case 'arc':
switch ($name) {
case 'empty':
$this->setBaseCommitExplanation(
pht(
"you specified '%s' in your %s 'base' configuration.",
$rule,
$source));
return self::GIT_MAGIC_ROOT_COMMIT;
case 'amended':
$text = $this->getCommitMessage('HEAD');
$message = ArcanistDifferentialCommitMessage::newFromRawCorpus(
$text);
if ($message->getRevisionID()) {
$this->setBaseCommitExplanation(
pht(
"HEAD has been amended with 'Differential Revision:', ".
"as specified by '%s' in your %s 'base' configuration.",
$rule,
$source));
return 'HEAD^';
}
break;
case 'upstream':
list($err, $upstream) = $this->execManualLocal(
'rev-parse --abbrev-ref --symbolic-full-name %s',
'@{upstream}');
if (!$err) {
$upstream = rtrim($upstream);
list($upstream_merge_base) = $this->execxLocal(
'merge-base %s HEAD',
$upstream);
$upstream_merge_base = rtrim($upstream_merge_base);
$this->setBaseCommitExplanation(
pht(
"it is the merge-base of the upstream of the current branch ".
"and HEAD, and matched the rule '%s' in your %s ".
"'base' configuration.",
$rule,
$source));
return $upstream_merge_base;
}
break;
case 'this':
$this->setBaseCommitExplanation(
pht(
"you specified '%s' in your %s 'base' configuration.",
$rule,
$source));
return 'HEAD^';
}
default:
return null;
}
return null;
}