private function getCommitMessageFromUser()

in src/workflow/ArcanistDiffWorkflow.php [1770:1970]


  private function getCommitMessageFromUser() {
    $conduit = $this->getConduit();

    $template = null;

    if (!$this->getArgument('verbatim')) {
      $saved = $this->readScratchFile('create-message');
      if ($saved) {
        $where = $this->getReadableScratchFilePath('create-message');

        $preview = explode("\n", $saved);
        $preview = array_shift($preview);
        $preview = trim($preview);
        $preview = id(new PhutilUTF8StringTruncator())
          ->setMaximumGlyphs(64)
          ->truncateString($preview);

        if ($preview) {
          $preview = pht('Message begins:')."\n\n       {$preview}\n\n";
        } else {
          $preview = null;
        }

        echo pht(
          "You have a saved revision message in '%s'.\n%s".
          "You can use this message, or discard it.",
          $where,
          $preview);

        $use = phutil_console_confirm(
          pht('Do you want to use this message?'),
          $default_no = false);
        if ($use) {
          $template = $saved;
        } else {
          $this->removeScratchFile('create-message');
        }
      }
    }

    $template_is_default = false;
    $notes = array();
    $included = array();

    list($fields, $notes, $included_commits) = $this->getDefaultCreateFields();
    if ($template) {
      $fields = array();
      $notes = array();
    } else {
      if (!$fields) {
        $template_is_default = true;
      }

      if ($notes) {
        $commit = head($this->getRepositoryAPI()->getLocalCommitInformation());
        $template = $commit['message'];
      } else {
        $revert_plan_check_paths = $this->getRevertPlanCheckPaths();
        if (!array_key_exists('revertPlan', $fields)
          && !is_null($revert_plan_check_paths)
          && $this->modifiesPath($revert_plan_check_paths)) {
          $fields['revertPlan'] = $this->getRevertPlan();
        }
        $template = $conduit->callMethodSynchronous(
          'differential.getcommitmessage',
          array(
            'revision_id' => null,
            'edit'        => 'create',
            'fields'      => $fields,
          ));
      }
    }

    $old_message = $template;

    $included = array();
    if ($included_commits) {
      foreach ($included_commits as $commit) {
        $included[] = '        '.$commit;
      }

      if (!$this->isRawDiffSource()) {
        $message = pht(
          'Included commits in branch %s:',
          $this->getRepositoryAPI()->getBranchName());
      } else {
        $message = pht('Included commits:');
      }
      $included = array_merge(
        array(
          '',
          $message,
          '',
        ),
        $included);
    }

    $issues = array_merge(
      array(
        pht('NEW DIFFERENTIAL REVISION'),
        pht('Describe the changes in this new revision.'),
      ),
      $included,
      array(
        '',
        pht(
          'arc could not identify any existing revision in your working copy.'),
        pht('If you intended to update an existing revision, use:'),
        '',
        '  $ arc diff --update <revision>',
      ));
    if ($notes) {
      $issues = array_merge($issues, array(''), $notes);
    }

    $done = false;
    $first = true;
    while (!$done) {
      $template = rtrim($template, "\r\n")."\n\n";
      foreach ($issues as $issue) {
        $template .= rtrim('# '.$issue)."\n";
      }
      $template .= "\n";

      if ($first && $this->getArgument('verbatim') && !$template_is_default) {
        $new_template = $template;
      } else {
        $new_template = $this->newInteractiveEditor($template)
          ->setName('new-commit')
          ->editInteractively();
      }
      $first = false;

      if ($template_is_default && ($new_template == $template)) {
        throw new ArcanistUsageException(pht('Template not edited.'));
      }

      $template = ArcanistCommentRemover::removeComments($new_template);

      // With --raw-command, we may not have a repository API.
      if ($this->hasRepositoryAPI()) {
        $repository_api = $this->getRepositoryAPI();
        // special check for whether to amend here. optimizes a common git
        // workflow. we can't do this for mercurial because the mq extension
        // is popular and incompatible with hg commit --amend ; see T2011.
        $should_amend = (count($included_commits) == 1 &&
                         $repository_api instanceof ArcanistGitAPI &&
                         $this->shouldAmend());
      } else {
        $should_amend = false;
      }

      if ($should_amend) {
        $wrote = (rtrim($old_message) != rtrim($template));
        if ($wrote) {
          $repository_api->amendCommit($template);
          $where = pht('commit message');
        }
      } else {
        $wrote = $this->writeScratchFile('create-message', $template);
        $where = "'".$this->getReadableScratchFilePath('create-message')."'";
      }

      try {
        $message = ArcanistDifferentialCommitMessage::newFromRawCorpus(
          $template);
        $message->pullDataFromConduit($conduit);
        $this->validateCommitMessage($message);
        $done = true;
      } catch (ArcanistDifferentialCommitMessageParserException $ex) {
        echo pht('Commit message has errors:')."\n\n";
        $issues = array(pht('Resolve these errors:'));
        foreach ($ex->getParserErrors() as $error) {
          echo phutil_console_wrap("- ".$error."\n", 6);
          $issues[] = '  - '.$error;
        }
        echo "\n";
        echo pht('You must resolve these errors to continue.');
        $again = phutil_console_confirm(
          pht('Do you want to edit the message?'),
          $default_no = false);
        if ($again) {
          // Keep going.
        } else {
          $saved = null;
          if ($wrote) {
            $saved = pht('A copy was saved to %s.', $where);
          }
          throw new ArcanistUsageException(
            pht('Message has unresolved errors.')." {$saved}");
        }
      } catch (Exception $ex) {
        if ($wrote) {
          echo phutil_console_wrap(pht('(Message saved to %s.)', $where)."\n");
        }
        throw $ex;
      }
    }

    return $message;
  }