public function resolveBaseCommitRule()

in src/repository/api/ArcanistMercurialAPI.php [816:972]


  public function resolveBaseCommitRule($rule, $source) {
    list($type, $name) = explode(':', $rule, 2);

    // NOTE: This function MUST return node hashes or symbolic commits (like
    // branch names or the word "tip"), not revsets. This includes ".^" and
    // similar, which a revset, not a symbolic commit identifier. If you return
    // a revset it will be escaped later and looked up literally.

    switch ($type) {
      case 'hg':
        $matches = null;
        if (preg_match('/^gca\((.+)\)$/', $name, $matches)) {
          list($err, $merge_base) = $this->execManualLocal(
            'log --template={node} --rev %s',
            sprintf('ancestor(., %s)', $matches[1]));
          if (!$err) {
            $this->setBaseCommitExplanation(
              pht(
                "it is the greatest common ancestor of '%s' and %s, as ".
                "specified by '%s' in your %s 'base' configuration.",
                $matches[1],
                '.',
                $rule,
                $source));
            return trim($merge_base);
          }
        } else {
          list($err, $commit) = $this->execManualLocal(
            'log --template {node} --rev %s',
            hgsprintf('%s', $name));

          if ($err) {
            list($err, $commit) = $this->execManualLocal(
              'log --template {node} --rev %s',
              $name);
          }
          if (!$err) {
            $this->setBaseCommitExplanation(
              pht(
                "it is specified by '%s' in your %s 'base' configuration.",
                $rule,
                $source));
            return trim($commit);
          }
        }
        break;
      case 'arc':
        switch ($name) {
          case 'empty':
            $this->setBaseCommitExplanation(
              pht(
                "you specified '%s' in your %s 'base' configuration.",
                $rule,
                $source));
            return 'null';
          case 'outgoing':
            list($err, $outgoing_base) = $this->execManualLocal(
              'log --template={node} --rev %s',
              'limit(reverse(ancestors(.) - outgoing()), 1)');
            if (!$err) {
              $this->setBaseCommitExplanation(
                pht(
                  "it is the first ancestor of the working copy that is not ".
                  "outgoing, and it matched the rule %s in your %s ".
                  "'base' configuration.",
                  $rule,
                  $source));
              return trim($outgoing_base);
            }
          case 'amended':
            $text = $this->getCommitMessage('.');
            $message = ArcanistDifferentialCommitMessage::newFromRawCorpus(
              $text);
            if ($message->getRevisionID()) {
              $this->setBaseCommitExplanation(
                pht(
                  "'%s' has been amended with 'Differential Revision:', ".
                  "as specified by '%s' in your %s 'base' configuration.",
                  '.'.
                  $rule,
                  $source));
              // NOTE: This should be safe because Mercurial doesn't support
              // amend until 2.2.
              return $this->getCanonicalRevisionName('.^');
            }
            break;
          case 'bookmark':
            $revset =
              'limit('.
              '  sort('.
              '    (ancestors(.) and bookmark() - .) or'.
              '    (ancestors(.) - outgoing()), '.
              '  -rev),'.
              '1)';
            list($err, $bookmark_base) = $this->execManualLocal(
              'log --template={node} --rev %s',
              $revset);
            if (!$err) {
              $this->setBaseCommitExplanation(
                pht(
                  "it is the first ancestor of %s that either has a bookmark, ".
                  "or is already in the remote and it matched the rule %s in ".
                  "your %s 'base' configuration",
                  '.',
                  $rule,
                  $source));
              return trim($bookmark_base);
            }
            break;
          case 'this':
            $this->setBaseCommitExplanation(
              pht(
                "you specified '%s' in your %s 'base' configuration.",
                $rule,
                $source));
            return $this->getCanonicalRevisionName('.^');
          default:
            if (preg_match('/^nodiff\((.+)\)$/', $name, $matches)) {
              list($results) = $this->execxLocal(
                'log --template %s --rev %s',
                "{node}\1{desc}\2",
                sprintf('ancestor(.,%s)::.^', $matches[1]));
              $results = array_reverse(explode("\2", trim($results)));

              foreach ($results as $result) {
                if (empty($result)) {
                  continue;
                }

                list($node, $desc) = explode("\1", $result, 2);

                $message = ArcanistDifferentialCommitMessage::newFromRawCorpus(
                  $desc);
                if ($message->getRevisionID()) {
                  $this->setBaseCommitExplanation(
                    pht(
                      "it is the first ancestor of %s that has a diff and is ".
                      "the gca or a descendant of the gca with '%s', ".
                      "specified by '%s' in your %s 'base' configuration.",
                      '.',
                      $matches[1],
                      $rule,
                      $source));
                  return $node;
                }
              }
            }
            break;
          }
        break;
      default:
        return null;
    }

    return null;

  }