@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Use TransactionEditor when closing revisions in response to commits

Summary: Ref T2222. When we discover a commit associated with a revision, close it using modern transactions.

Test Plan: {F123848}

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2222

Differential Revision: https://secure.phabricator.com/D8441

+134 -55
+53 -13
src/applications/differential/editor/DifferentialTransactionEditor.php
··· 4 4 extends PhabricatorApplicationTransactionEditor { 5 5 6 6 private $heraldEmailPHIDs; 7 + private $changedPriorToCommitURI; 8 + private $isCloseByCommit; 9 + 10 + public function setIsCloseByCommit($is_close_by_commit) { 11 + $this->isCloseByCommit = $is_close_by_commit; 12 + return $this; 13 + } 14 + 15 + public function getIsCloseByCommit() { 16 + return $this->isCloseByCommit; 17 + } 18 + 19 + public function setChangedPriorToCommitURI($uri) { 20 + $this->changedPriorToCommitURI = $uri; 21 + return $this; 22 + } 23 + 24 + public function getChangedPriorToCommitURI() { 25 + return $this->changedPriorToCommitURI; 26 + } 7 27 8 28 public function getTransactionTypes() { 9 29 $types = parent::getTransactionTypes(); ··· 158 178 case PhabricatorTransactions::TYPE_EDGE: 159 179 return; 160 180 case DifferentialTransaction::TYPE_UPDATE: 161 - $object->setStatus($status_review); 181 + if (!$this->getIsCloseByCommit()) { 182 + $object->setStatus($status_review); 183 + } 162 184 // TODO: Update the `diffPHID` once we add that. 163 185 return; 164 186 case DifferentialTransaction::TYPE_ACTION: ··· 209 231 $results = parent::expandTransaction($object, $xaction); 210 232 switch ($xaction->getTransactionType()) { 211 233 case DifferentialTransaction::TYPE_UPDATE: 234 + if ($this->getIsCloseByCommit()) { 235 + // Don't bother with any of this if this update is a side effect of 236 + // commit detection. 237 + break; 238 + } 239 + 212 240 $new_accept = DifferentialReviewerStatus::STATUS_ACCEPTED; 213 241 $new_reject = DifferentialReviewerStatus::STATUS_REJECTED; 214 242 $old_accept = DifferentialReviewerStatus::STATUS_ACCEPTED_OLDER; ··· 784 812 break; 785 813 786 814 case DifferentialAction::ACTION_CLOSE: 815 + // We force revisions closed when we discover a corresponding commit. 816 + // In this case, revisions are allowed to transition to closed from 817 + // any state. This is an automated action taken by the daemons. 787 818 788 - // TODO: Permit the daemons to take this action in all cases. 819 + if (!$this->getIsCloseByCommit()) { 820 + if (!$actor_is_author && !$always_allow_close) { 821 + return pht( 822 + "You can not close this revision because you do not own it. To ". 823 + "close a revision, you must be its owner."); 824 + } 789 825 790 - if (!$actor_is_author && !$always_allow_close) { 791 - return pht( 792 - "You can not close this revision because you do not own it. To ". 793 - "close a revision, you must be its owner."); 794 - } 795 - 796 - if ($revision_status != $status_accepted) { 797 - return pht( 798 - "You can not close this revision because it has not been ". 799 - "accepted. You can only close accepted revisions."); 826 + if ($revision_status != $status_accepted) { 827 + return pht( 828 + "You can not close this revision because it has not been ". 829 + "accepted. You can only close accepted revisions."); 830 + } 800 831 } 801 832 break; 802 833 } ··· 907 938 if ($xaction->getTransactionType() == $type_inline) { 908 939 $inlines[] = $xaction; 909 940 } 941 + } 942 + 943 + $changed_uri = $this->getChangedPriorToCommitURI(); 944 + if ($changed_uri) { 945 + $body->addTextSection( 946 + pht('CHANGED PRIOR TO COMMIT'), 947 + $changed_uri); 910 948 } 911 949 912 950 if ($inlines) { ··· 1099 1137 foreach ($xactions as $xaction) { 1100 1138 switch ($xaction->getTransactionType()) { 1101 1139 case DifferentialTransaction::TYPE_UPDATE: 1102 - return true; 1140 + if (!$this->getIsCloseByCommit()) { 1141 + return true; 1142 + } 1103 1143 } 1104 1144 } 1105 1145
+2
src/applications/metamta/contentsource/PhabricatorContentSource.php
··· 12 12 const SOURCE_CONSOLE = 'console'; 13 13 const SOURCE_HERALD = 'herald'; 14 14 const SOURCE_LEGACY = 'legacy'; 15 + const SOURCE_DAEMON = 'daemon'; 15 16 16 17 private $source; 17 18 private $params = array(); ··· 72 73 self::SOURCE_CONSOLE => pht('Console'), 73 74 self::SOURCE_LEGACY => pht('Legacy'), 74 75 self::SOURCE_HERALD => pht('Herald'), 76 + self::SOURCE_DAEMON => pht('Daemons'), 75 77 self::SOURCE_UNKNOWN => pht('Old World'), 76 78 ); 77 79 }
+79 -42
src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php
··· 78 78 $should_autoclose = $repository->shouldAutocloseCommit($commit, $data); 79 79 80 80 if ($revision_id) { 81 - $lock = PhabricatorGlobalLock::newLock(get_class($this).':'.$revision_id); 82 - $lock->lock(5 * 60); 83 - 84 81 // TODO: Check if a more restrictive viewer could be set here 85 - $revision = id(new DifferentialRevisionQuery()) 82 + $revision_query = id(new DifferentialRevisionQuery()) 86 83 ->withIDs(array($revision_id)) 87 84 ->setViewer(PhabricatorUser::getOmnipotentUser()) 88 - ->needRelationships(true) 89 85 ->needReviewerStatus(true) 90 - ->executeOne(); 86 + ->needActiveDiffs(true); 87 + 88 + // TODO: Remove this once we swap to new CustomFields. This is only 89 + // required by the old FieldSpecifications, lower on in this worker. 90 + $revision_query->needRelationships(true); 91 + 92 + $revision = $revision_query->executeOne(); 91 93 92 94 if ($revision) { 93 95 $commit_drev = PhabricatorEdgeConfig::TYPE_COMMIT_HAS_DREV; ··· 112 114 $committer_phid, 113 115 $author_phid, 114 116 $revision->getAuthorPHID()); 117 + 115 118 $actor = id(new PhabricatorUser()) 116 119 ->loadOneWhere('phid = %s', $actor_phid); 117 120 118 - $diff = $this->attachToRevision($revision, $actor_phid); 119 - 120 - $editor = new DifferentialCommentEditor( 121 - $revision, 122 - DifferentialAction::ACTION_CLOSE); 123 - $editor->setActor($actor); 124 - $editor->setIsDaemonWorkflow(true); 125 - 126 - $vs_diff = $this->loadChangedByCommit($diff); 127 - if ($vs_diff) { 128 - $data->setCommitDetail('vsDiff', $vs_diff->getID()); 129 - 130 - $changed_by_commit = PhabricatorEnv::getProductionURI( 131 - '/D'.$revision->getID(). 132 - '?vs='.$vs_diff->getID(). 133 - '&id='.$diff->getID(). 134 - '#toc'); 135 - $editor->setChangedByCommit($changed_by_commit); 136 - } 137 - 138 121 $commit_name = $repository->formatCommitName( 139 122 $commit->getCommitIdentifier()); 140 123 ··· 148 131 $data->getAuthorName(), 149 132 $actor); 150 133 151 - $info = array(); 152 - $info[] = "authored by {$author_name}"; 153 134 if ($committer_name && ($committer_name != $author_name)) { 154 - $info[] = "committed by {$committer_name}"; 135 + $message = pht( 136 + 'Closed by commit %s (authored by %s, committed by %s).', 137 + $commit_name, 138 + $author_name, 139 + $committer_name); 140 + } else { 141 + $message = pht( 142 + 'Closed by commit %s (authored by %s).', 143 + $commit_name, 144 + $author_name); 145 + } 146 + 147 + $diff = $this->generateFinalDiff($revision, $actor_phid); 148 + 149 + $vs_diff = $this->loadChangedByCommit($revision, $diff); 150 + $changed_uri = null; 151 + if ($vs_diff) { 152 + $data->setCommitDetail('vsDiff', $vs_diff->getID()); 153 + 154 + $changed_uri = PhabricatorEnv::getProductionURI( 155 + '/D'.$revision->getID(). 156 + '?vs='.$vs_diff->getID(). 157 + '&id='.$diff->getID(). 158 + '#toc'); 155 159 } 156 - $info = implode(', ', $info); 157 160 158 - $editor 159 - ->setMessage("Closed by commit {$commit_name} ({$info}).") 160 - ->save(); 161 - } 161 + $xactions = array(); 162 162 163 - } 163 + $xactions[] = id(new DifferentialTransaction()) 164 + ->setTransactionType(DifferentialTransaction::TYPE_ACTION) 165 + ->setNewValue(DifferentialAction::ACTION_CLOSE); 164 166 165 - $lock->unlock(); 167 + $xactions[] = id(new DifferentialTransaction()) 168 + ->setTransactionType(DifferentialTransaction::TYPE_UPDATE) 169 + ->setIgnoreOnNoEffect(true) 170 + ->setNewValue($diff->getPHID()); 171 + 172 + $xactions[] = id(new DifferentialTransaction()) 173 + ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) 174 + ->setIgnoreOnNoEffect(true) 175 + ->attachComment( 176 + id(new DifferentialTransactionComment()) 177 + ->setContent($message)); 178 + 179 + $content_source = PhabricatorContentSource::newForSource( 180 + PhabricatorContentSource::SOURCE_DAEMON, 181 + array()); 182 + 183 + $editor = id(new DifferentialTransactionEditor()) 184 + ->setActor($actor) 185 + ->setContinueOnMissingFields(true) 186 + ->setContentSource($content_source) 187 + ->setChangedPriorToCommitURI($changed_uri) 188 + ->setIsCloseByCommit(true); 189 + 190 + try { 191 + $editor->applyTransactions($revision, $xactions); 192 + } catch (PhabricatorApplicationTransactionNoEffectException $ex) { 193 + // NOTE: We've marked transactions other than the CLOSE transaction 194 + // as ignored when they don't have an effect, so this means that we 195 + // lost a race to close the revision. That's perfectly fine, we can 196 + // just continue normally. 197 + } 198 + } 199 + } 166 200 } 167 201 168 202 if ($should_autoclose) { 203 + // TODO: When this is moved to CustomFields, remove the additional 204 + // call above in query construction. 169 205 $fields = DifferentialFieldSelector::newSelector() 170 206 ->getFieldSpecifications(); 171 207 foreach ($fields as $key => $field) { ··· 200 236 return '@'.$handle->getName(); 201 237 } 202 238 203 - private function attachToRevision( 239 + private function generateFinalDiff( 204 240 DifferentialRevision $revision, 205 241 $actor_phid) { 206 242 ··· 232 268 } 233 269 234 270 $diff = DifferentialDiff::newFromRawChanges($changes) 235 - ->setRevisionID($revision->getID()) 271 + ->setRepositoryPHID($this->repository->getPHID()) 236 272 ->setAuthorPHID($actor_phid) 237 273 ->setCreationMethod('commit') 238 274 ->setSourceControlSystem($this->repository->getVersionControlSystem()) ··· 264 300 } 265 301 266 302 // TODO: Attach binary files. 267 - 268 - $revision->setLineCount($diff->getLineCount()); 269 303 270 304 return $diff->save(); 271 305 } 272 306 273 - private function loadChangedByCommit(DifferentialDiff $diff) { 307 + private function loadChangedByCommit( 308 + DifferentialRevision $revision, 309 + DifferentialDiff $diff) { 310 + 274 311 $repository = $this->repository; 275 312 276 313 $vs_changesets = array(); 277 314 $vs_diff = id(new DifferentialDiff())->loadOneWhere( 278 315 'revisionID = %d AND creationMethod != %s ORDER BY id DESC LIMIT 1', 279 - $diff->getRevisionID(), 316 + $revision->getID(), 280 317 'commit'); 281 318 foreach ($vs_diff->loadChangesets() as $changeset) { 282 319 $path = $changeset->getAbsoluteRepositoryPath($repository, $vs_diff);