protected function buildBaseCommit()

in src/repository/api/ArcanistGitAPI.php [209:396]


  protected function buildBaseCommit($symbolic_commit) {
    if ($symbolic_commit !== null) {
      if ($symbolic_commit == self::GIT_MAGIC_ROOT_COMMIT) {
        $this->setBaseCommitExplanation(
          pht('you explicitly specified the empty tree.'));
        return $symbolic_commit;
      }

      list($err, $merge_base) = $this->execManualLocal(
        'merge-base %s %s',
        $symbolic_commit,
        $this->getHeadCommit());
      if ($err) {
        throw new ArcanistUsageException(
          pht(
            "Unable to find any git commit named '%s' in this repository.",
            $symbolic_commit));
      }

      if ($this->symbolicHeadCommit === null) {
        $this->setBaseCommitExplanation(
          pht(
            "it is the merge-base of the explicitly specified base commit ".
            "'%s' and HEAD.",
            $symbolic_commit));
      } else {
        $this->setBaseCommitExplanation(
          pht(
            "it is the merge-base of the explicitly specified base commit ".
            "'%s' and the explicitly specified head commit '%s'.",
            $symbolic_commit,
            $this->symbolicHeadCommit));
      }

      return trim($merge_base);
    }

    // Detect zero-commit or one-commit repositories. There is only one
    // relative-commit value that makes any sense in these repositories: the
    // empty tree.
    list($err) = $this->execManualLocal('rev-parse --verify HEAD^');
    if ($err) {
      list($err) = $this->execManualLocal('rev-parse --verify HEAD');
      if ($err) {
        $this->repositoryHasNoCommits = true;
      }

      if ($this->repositoryHasNoCommits) {
        $this->setBaseCommitExplanation(pht('the repository has no commits.'));
      } else {
        $this->setBaseCommitExplanation(
          pht('the repository has only one commit.'));
      }

      return self::GIT_MAGIC_ROOT_COMMIT;
    }

    if ($this->getBaseCommitArgumentRules() ||
        $this->getConfigurationManager()->getConfigFromAnySource('base')) {
      $base = $this->resolveBaseCommit();
      if (!$base) {
        throw new ArcanistUsageException(
          pht(
            "None of the rules in your 'base' configuration matched a valid ".
            "commit. Adjust rules or specify which commit you want to use ".
            "explicitly."));
      }
      return $base;
    }

    $do_write = false;
    $default_relative = null;
    $working_copy = $this->getWorkingCopyIdentity();

    if ($working_copy) {
      $default_relative = $working_copy->getProjectConfig(
        'git.default-relative-commit');
      $this->setBaseCommitExplanation(
        pht(
          "it is the merge-base of '%s' and HEAD, as specified in '%s' in ".
          "'%s'. This setting overrides other settings.",
          $default_relative,
          'git.default-relative-commit',
          '.arcconfig'));
    }

    // UBER CODE
    if ($this->readScratchFile('uses-arc-flow') === 'true') {
      $default_relative = null;
    }
    // UBER CODE END

    if (!$default_relative) {
      list($err, $upstream) = $this->execManualLocal(
        'rev-parse --abbrev-ref --symbolic-full-name %s',
        '@{upstream}');

      if (!$err) {
        $default_relative = trim($upstream);
        $this->setBaseCommitExplanation(
          pht(
            "it is the merge-base of '%s' (the Git upstream ".
            "of the current branch) HEAD.",
            $default_relative));
      }
    }

    if (!$default_relative) {
      $default_relative = $this->readScratchFile('default-relative-commit');
      $default_relative = trim($default_relative);
      if ($default_relative) {
        $this->setBaseCommitExplanation(
          pht(
            "it is the merge-base of '%s' and HEAD, as specified in '%s'.",
            $default_relative,
            '.git/arc/default-relative-commit'));
      }
    }

    if (!$default_relative) {

      // TODO: Remove the history lesson soon.

      echo phutil_console_format(
        "<bg:green>** %s **</bg>\n\n",
        pht('Select a Default Commit Range'));
      echo phutil_console_wrap(
        pht(
          "You're running a command which operates on a range of revisions ".
          "(usually, from some revision to HEAD) but have not specified the ".
          "revision that should determine the start of the range.\n\n".
          "Previously, arc assumed you meant '%s' when you did not specify ".
          "a start revision, but this behavior does not make much sense in ".
          "most workflows outside of Facebook's historic %s workflow.\n\n".
          "arc no longer assumes '%s'. You must specify a relative commit ".
          "explicitly when you invoke a command (e.g., `%s`, not just `%s`) ".
          "or select a default for this working copy.\n\nIn most cases, the ".
          "best default is '%s'. You can also select '%s' to preserve the ".
          "old behavior, or some other remote or branch. But you almost ".
          "certainly want to select 'origin/master'.\n\n".
          "(Technically: the merge-base of the selected revision and HEAD is ".
          "used to determine the start of the commit range.)",
          'HEAD^',
          'git-svn',
          'HEAD^',
          'arc diff HEAD^',
          'arc diff',
          'origin/master',
          'HEAD^'));

      $prompt = pht('What default do you want to use? [origin/master]');
      $default = phutil_console_prompt($prompt);

      if (!strlen(trim($default))) {
        $default = 'origin/master';
      }

      $default_relative = $default;
      $do_write = true;
    }

    list($object_type) = $this->execxLocal(
      'cat-file -t %s',
      $default_relative);

    if (trim($object_type) !== 'commit') {
      throw new Exception(
        pht(
          "Relative commit '%s' is not the name of a commit!",
          $default_relative));
    }

    if ($do_write) {
      // Don't perform this write until we've verified that the object is a
      // valid commit name.
      $this->writeScratchFile('default-relative-commit', $default_relative);
      $this->setBaseCommitExplanation(
        pht(
          "it is the merge-base of '%s' and HEAD, as you just specified.",
          $default_relative));
    }

    list($merge_base) = $this->execxLocal(
      'merge-base %s HEAD',
      $default_relative);

    return trim($merge_base);
  }