public function getBarrierToLanding(PhabricatorUser $viewer, DifferentialRevision $revision)
 {
     $repository = $revision->getRepository();
     if (!$repository) {
         return array('title' => pht('No Repository'), 'body' => pht('This revision is not associated with a known repository. Only ' . 'revisions associated with a tracked repository can be landed ' . 'automatically.'));
     }
     if (!$repository->canPerformAutomation()) {
         return array('title' => pht('No Repository Automation'), 'body' => pht('The repository this revision is associated with ("%s") is not ' . 'configured to support automation. Configure automation for the ' . 'repository to enable revisions to be landed automatically.', $repository->getMonogram()));
     }
     // Check if this diff was pushed to a staging area.
     $diff = id(new DifferentialDiffQuery())->setViewer($viewer)->withIDs(array($revision->getActiveDiff()->getID()))->needProperties(true)->executeOne();
     // Older diffs won't have this property. They may still have been pushed.
     // At least for now, assume staging changes are present if the property
     // is missing. This should smooth the transition to the more formal
     // approach.
     $has_staging = $diff->hasDiffProperty('arc.staging');
     if ($has_staging) {
         $staging = $diff->getProperty('arc.staging');
         if (!is_array($staging)) {
             $staging = array();
         }
         $status = idx($staging, 'status');
         if ($status != ArcanistDiffWorkflow::STAGING_PUSHED) {
             return $this->getBarrierToLandingFromStagingStatus($status);
         }
     }
     // TODO: At some point we should allow installs to give "land reviewed
     // code" permission to more users than "push any commit", because it is
     // a much less powerful operation. For now, just require push so this
     // doesn't do anything users can't do on their own.
     $can_push = PhabricatorPolicyFilter::hasCapability($viewer, $repository, DiffusionPushCapability::CAPABILITY);
     if (!$can_push) {
         return array('title' => pht('Unable to Push'), 'body' => pht('You do not have permission to push to the repository this ' . 'revision is associated with ("%s"), so you can not land it.', $repository->getMonogram()));
     }
     $status_accepted = ArcanistDifferentialRevisionStatus::ACCEPTED;
     if ($revision->getStatus() != $status_accepted) {
         switch ($revision->getStatus()) {
             case ArcanistDifferentialRevisionStatus::CLOSED:
                 return array('title' => pht('Revision Closed'), 'body' => pht('This revision has already been closed. Only open, accepted ' . 'revisions may land.'));
             case ArcanistDifferentialRevisionStatus::ABANDONED:
                 return array('title' => pht('Revision Abandoned'), 'body' => pht('This revision has been abandoned. Only accepted revisions ' . 'may land.'));
             default:
                 return array('title' => pht('Revision Not Accepted'), 'body' => pht('This revision is still under review. Only revisions which ' . 'have been accepted may land.'));
         }
     }
     // Check for other operations. Eventually this should probably be more
     // general (e.g., it's OK to land to multiple different branches
     // simultaneously) but just put this in as a sanity check for now.
     $other_operations = id(new DrydockRepositoryOperationQuery())->setViewer($viewer)->withObjectPHIDs(array($revision->getPHID()))->withOperationTypes(array($this->getOperationConstant()))->withOperationStates(array(DrydockRepositoryOperation::STATE_WAIT, DrydockRepositoryOperation::STATE_WORK, DrydockRepositoryOperation::STATE_DONE))->execute();
     if ($other_operations) {
         $any_done = false;
         foreach ($other_operations as $operation) {
             if ($operation->isDone()) {
                 $any_done = true;
                 break;
             }
         }
         if ($any_done) {
             return array('title' => pht('Already Complete'), 'body' => pht('This revision has already landed.'));
         } else {
             return array('title' => pht('Already In Flight'), 'body' => pht('This revision is already landing.'));
         }
     }
     return null;
 }
 public function updateRevisionWithCommit(DifferentialRevision $revision, PhabricatorRepositoryCommit $commit, array $more_xactions, PhabricatorContentSource $content_source)
 {
     $viewer = $this->getViewer();
     $result_data = array();
     $new_diff = $this->newDiffFromCommit($commit);
     $old_diff = $revision->getActiveDiff();
     $changed_uri = null;
     if ($old_diff) {
         $old_diff = id(new DifferentialDiffQuery())->setViewer($viewer)->withIDs(array($old_diff->getID()))->needChangesets(true)->executeOne();
         if ($old_diff) {
             $has_changed = $this->isDiffChangedBeforeCommit($commit, $old_diff, $new_diff);
             if ($has_changed) {
                 $result_data['vsDiff'] = $old_diff->getID();
                 $revision_monogram = $revision->getMonogram();
                 $old_id = $old_diff->getID();
                 $new_id = $new_diff->getID();
                 $changed_uri = "/{$revision_monogram}?vs={$old_id}&id={$new_id}#toc";
                 $changed_uri = PhabricatorEnv::getProductionURI($changed_uri);
             }
         }
     }
     $xactions = array();
     $xactions[] = id(new DifferentialTransaction())->setTransactionType(DifferentialTransaction::TYPE_UPDATE)->setIgnoreOnNoEffect(true)->setNewValue($new_diff->getPHID())->setMetadataValue('isCommitUpdate', true);
     foreach ($more_xactions as $more_xaction) {
         $xactions[] = $more_xaction;
     }
     $editor = id(new DifferentialTransactionEditor())->setActor($viewer)->setContinueOnMissingFields(true)->setContentSource($content_source)->setChangedPriorToCommitURI($changed_uri)->setIsCloseByCommit(true);
     $author_phid = $this->getAuthorPHID();
     if ($author_phid !== null) {
         $editor->setActingAsPHID($author_phid);
     }
     try {
         $editor->applyTransactions($revision, $xactions);
     } catch (PhabricatorApplicationTransactionNoEffectException $ex) {
         // NOTE: We've marked transactions other than the CLOSE transaction
         // as ignored when they don't have an effect, so this means that we
         // lost a race to close the revision. That's perfectly fine, we can
         // just continue normally.
     }
     return $result_data;
 }