@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.

Move commit attachment to a separate CLI command

Summary:
Ref T9319. See D14967. As before, this is making a deeply-buried, complex operation easier to test by providing a CLI command.

This adds `bin/differential attach-commit rXnnnn Dnnnn` to pretend that `rXnnnn` was just committed and matched `Dnnnn`.

Test Plan:
- Ran `bin/differential attach-commit X Y` for several different values, saw updates in the UI.
- Faked the message parser to make sure stuff still worked there.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9319

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

+273 -143
+2
src/__phutil_library_map__.php
··· 2143 2143 'PhabricatorDiffPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorDiffPreferencesSettingsPanel.php', 2144 2144 'PhabricatorDifferenceEngine' => 'infrastructure/diff/PhabricatorDifferenceEngine.php', 2145 2145 'PhabricatorDifferentialApplication' => 'applications/differential/application/PhabricatorDifferentialApplication.php', 2146 + 'PhabricatorDifferentialAttachCommitWorkflow' => 'applications/differential/management/PhabricatorDifferentialAttachCommitWorkflow.php', 2146 2147 'PhabricatorDifferentialConfigOptions' => 'applications/differential/config/PhabricatorDifferentialConfigOptions.php', 2147 2148 'PhabricatorDifferentialExtractWorkflow' => 'applications/differential/management/PhabricatorDifferentialExtractWorkflow.php', 2148 2149 'PhabricatorDifferentialManagementWorkflow' => 'applications/differential/management/PhabricatorDifferentialManagementWorkflow.php', ··· 6379 6380 'PhabricatorDiffPreferencesSettingsPanel' => 'PhabricatorSettingsPanel', 6380 6381 'PhabricatorDifferenceEngine' => 'Phobject', 6381 6382 'PhabricatorDifferentialApplication' => 'PhabricatorApplication', 6383 + 'PhabricatorDifferentialAttachCommitWorkflow' => 'PhabricatorDifferentialManagementWorkflow', 6382 6384 'PhabricatorDifferentialConfigOptions' => 'PhabricatorApplicationConfigOptions', 6383 6385 'PhabricatorDifferentialExtractWorkflow' => 'PhabricatorDifferentialManagementWorkflow', 6384 6386 'PhabricatorDifferentialManagementWorkflow' => 'PhabricatorManagementWorkflow',
+170
src/applications/differential/engine/DifferentialDiffExtractionEngine.php
··· 87 87 return $diff->save(); 88 88 } 89 89 90 + public function isDiffChangedBeforeCommit( 91 + PhabricatorRepositoryCommit $commit, 92 + DifferentialDiff $old, 93 + DifferentialDiff $new) { 94 + 95 + $viewer = $this->getViewer(); 96 + $repository = $commit->getRepository(); 97 + $identifier = $commit->getCommitIdentifier(); 98 + 99 + $vs_changesets = array(); 100 + foreach ($old->getChangesets() as $changeset) { 101 + $path = $changeset->getAbsoluteRepositoryPath($repository, $old); 102 + $path = ltrim($path, '/'); 103 + $vs_changesets[$path] = $changeset; 104 + } 105 + 106 + $changesets = array(); 107 + foreach ($new->getChangesets() as $changeset) { 108 + $path = $changeset->getAbsoluteRepositoryPath($repository, $new); 109 + $path = ltrim($path, '/'); 110 + $changesets[$path] = $changeset; 111 + } 112 + 113 + if (array_fill_keys(array_keys($changesets), true) != 114 + array_fill_keys(array_keys($vs_changesets), true)) { 115 + return true; 116 + } 117 + 118 + $file_phids = array(); 119 + foreach ($vs_changesets as $changeset) { 120 + $metadata = $changeset->getMetadata(); 121 + $file_phid = idx($metadata, 'new:binary-phid'); 122 + if ($file_phid) { 123 + $file_phids[$file_phid] = $file_phid; 124 + } 125 + } 126 + 127 + $files = array(); 128 + if ($file_phids) { 129 + $files = id(new PhabricatorFileQuery()) 130 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 131 + ->withPHIDs($file_phids) 132 + ->execute(); 133 + $files = mpull($files, null, 'getPHID'); 134 + } 135 + 136 + foreach ($changesets as $path => $changeset) { 137 + $vs_changeset = $vs_changesets[$path]; 138 + 139 + $file_phid = idx($vs_changeset->getMetadata(), 'new:binary-phid'); 140 + if ($file_phid) { 141 + if (!isset($files[$file_phid])) { 142 + return true; 143 + } 144 + 145 + $drequest = DiffusionRequest::newFromDictionary(array( 146 + 'user' => $viewer, 147 + 'repository' => $repository, 148 + 'commit' => $identifier, 149 + 'path' => $path, 150 + )); 151 + 152 + $corpus = DiffusionFileContentQuery::newFromDiffusionRequest($drequest) 153 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 154 + ->loadFileContent() 155 + ->getCorpus(); 156 + 157 + if ($files[$file_phid]->loadFileData() != $corpus) { 158 + return true; 159 + } 160 + } else { 161 + $context = implode("\n", $changeset->makeChangesWithContext()); 162 + $vs_context = implode("\n", $vs_changeset->makeChangesWithContext()); 163 + 164 + // We couldn't just compare $context and $vs_context because following 165 + // diffs will be considered different: 166 + // 167 + // -(empty line) 168 + // -echo 'test'; 169 + // (empty line) 170 + // 171 + // (empty line) 172 + // -echo "test"; 173 + // -(empty line) 174 + 175 + $hunk = id(new DifferentialModernHunk())->setChanges($context); 176 + $vs_hunk = id(new DifferentialModernHunk())->setChanges($vs_context); 177 + if ($hunk->makeOldFile() != $vs_hunk->makeOldFile() || 178 + $hunk->makeNewFile() != $vs_hunk->makeNewFile()) { 179 + return true; 180 + } 181 + } 182 + } 183 + 184 + return false; 185 + } 186 + 187 + public function updateRevisionWithCommit( 188 + DifferentialRevision $revision, 189 + PhabricatorRepositoryCommit $commit, 190 + array $more_xactions, 191 + PhabricatorContentSource $content_source) { 192 + 193 + $viewer = $this->getViewer(); 194 + $result_data = array(); 195 + 196 + $new_diff = $this->newDiffFromCommit($commit); 197 + 198 + $old_diff = $revision->getActiveDiff(); 199 + $changed_uri = null; 200 + if ($old_diff) { 201 + $old_diff = id(new DifferentialDiffQuery()) 202 + ->setViewer($viewer) 203 + ->withIDs(array($old_diff->getID())) 204 + ->needChangesets(true) 205 + ->executeOne(); 206 + if ($old_diff) { 207 + $has_changed = $this->isDiffChangedBeforeCommit( 208 + $commit, 209 + $old_diff, 210 + $new_diff); 211 + if ($has_changed) { 212 + $result_data['vsDiff'] = $old_diff->getID(); 213 + 214 + $revision_monogram = $revision->getMonogram(); 215 + $old_id = $old_diff->getID(); 216 + $new_id = $new_diff->getID(); 217 + 218 + $changed_uri = "/{$revision_monogram}?vs={$old_id}&id={$new_id}#toc"; 219 + $changed_uri = PhabricatorEnv::getProductionURI($changed_uri); 220 + } 221 + } 222 + } 223 + 224 + $xactions = array(); 225 + 226 + $xactions[] = id(new DifferentialTransaction()) 227 + ->setTransactionType(DifferentialTransaction::TYPE_UPDATE) 228 + ->setIgnoreOnNoEffect(true) 229 + ->setNewValue($new_diff->getPHID()) 230 + ->setMetadataValue('isCommitUpdate', true); 231 + 232 + foreach ($more_xactions as $more_xaction) { 233 + $xactions[] = $more_xaction; 234 + } 235 + 236 + $editor = id(new DifferentialTransactionEditor()) 237 + ->setActor($viewer) 238 + ->setContinueOnMissingFields(true) 239 + ->setContentSource($content_source) 240 + ->setChangedPriorToCommitURI($changed_uri) 241 + ->setIsCloseByCommit(true); 242 + 243 + $author_phid = $this->getAuthorPHID(); 244 + if ($author_phid !== null) { 245 + $editor->setActingAsPHID($author_phid); 246 + } 247 + 248 + try { 249 + $editor->applyTransactions($revision, $xactions); 250 + } catch (PhabricatorApplicationTransactionNoEffectException $ex) { 251 + // NOTE: We've marked transactions other than the CLOSE transaction 252 + // as ignored when they don't have an effect, so this means that we 253 + // lost a race to close the revision. That's perfectly fine, we can 254 + // just continue normally. 255 + } 256 + 257 + return $result_data; 258 + } 259 + 90 260 }
+90
src/applications/differential/management/PhabricatorDifferentialAttachCommitWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorDifferentialAttachCommitWorkflow 4 + extends PhabricatorDifferentialManagementWorkflow { 5 + 6 + protected function didConstruct() { 7 + $this 8 + ->setName('attach-commit') 9 + ->setExamples('**attach-commit** __commit__ __revision__') 10 + ->setSynopsis(pht('Forcefully attach a commit to a revision.')) 11 + ->setArguments( 12 + array( 13 + array( 14 + 'name' => 'argv', 15 + 'wildcard' => true, 16 + 'help' => pht('Commit, and a revision to attach it to.'), 17 + ), 18 + )); 19 + } 20 + 21 + public function execute(PhutilArgumentParser $args) { 22 + $viewer = $this->getViewer(); 23 + 24 + $argv = $args->getArg('argv'); 25 + if (count($argv) !== 2) { 26 + throw new PhutilArgumentUsageException( 27 + pht('Specify a commit and a revision to attach it to.')); 28 + } 29 + 30 + $commit_name = head($argv); 31 + $revision_name = last($argv); 32 + 33 + $commit = id(new DiffusionCommitQuery()) 34 + ->setViewer($viewer) 35 + ->withIdentifiers(array($commit_name)) 36 + ->executeOne(); 37 + if (!$commit) { 38 + throw new PhutilArgumentUsageException( 39 + pht('Commit "%s" does not exist.', $commit_name)); 40 + } 41 + 42 + $revision = id(new PhabricatorObjectQuery()) 43 + ->setViewer($viewer) 44 + ->withNames(array($revision_name)) 45 + ->executeOne(); 46 + 47 + if (!$revision) { 48 + throw new PhutilArgumentUsageException( 49 + pht('Revision "%s" does not exist.', $revision_name)); 50 + } 51 + 52 + if (!($revision instanceof DifferentialRevision)) { 53 + throw new PhutilArgumentUsageException( 54 + pht('Object "%s" must be a Differential revision.', $revision_name)); 55 + } 56 + 57 + // Reload the revision to get the active diff. 58 + $revision = id(new DifferentialRevisionQuery()) 59 + ->setViewer($viewer) 60 + ->withIDs(array($revision->getID())) 61 + ->needActiveDiffs(true) 62 + ->executeOne(); 63 + 64 + $differential_phid = id(new PhabricatorDifferentialApplication()) 65 + ->getPHID(); 66 + 67 + $extraction_engine = id(new DifferentialDiffExtractionEngine()) 68 + ->setViewer($viewer) 69 + ->setAuthorPHID($differential_phid); 70 + 71 + $content_source = PhabricatorContentSource::newForSource( 72 + PhabricatorContentSource::SOURCE_CONSOLE, 73 + array()); 74 + 75 + $extraction_engine->updateRevisionWithCommit( 76 + $revision, 77 + $commit, 78 + array(), 79 + $content_source); 80 + 81 + echo tsprintf( 82 + "%s\n", 83 + pht( 84 + 'Attached "%s" to "%s".', 85 + $commit->getMonogram(), 86 + $revision->getMonogram())); 87 + } 88 + 89 + 90 + }
+11 -143
src/applications/repository/worker/commitmessageparser/PhabricatorRepositoryCommitMessageParserWorker.php
··· 216 216 $low_level_query->getRevisionMatchData()); 217 217 } 218 218 219 - $diff = id(new DifferentialDiffExtractionEngine()) 219 + $extraction_engine = id(new DifferentialDiffExtractionEngine()) 220 220 ->setViewer($actor) 221 - ->setAuthorPHID($acting_as_phid) 222 - ->newDiffFromCommit($commit); 223 - 224 - $vs_diff = $this->loadChangedByCommit($revision, $diff); 225 - $changed_uri = null; 226 - if ($vs_diff) { 227 - $data->setCommitDetail('vsDiff', $vs_diff->getID()); 228 - 229 - $changed_uri = PhabricatorEnv::getProductionURI( 230 - '/D'.$revision->getID(). 231 - '?vs='.$vs_diff->getID(). 232 - '&id='.$diff->getID(). 233 - '#toc'); 234 - } 235 - 236 - $xactions = array(); 237 - $xactions[] = id(new DifferentialTransaction()) 238 - ->setTransactionType(DifferentialTransaction::TYPE_UPDATE) 239 - ->setIgnoreOnNoEffect(true) 240 - ->setNewValue($diff->getPHID()) 241 - ->setMetadataValue('isCommitUpdate', true); 242 - $xactions[] = $commit_close_xaction; 221 + ->setAuthorPHID($acting_as_phid); 243 222 244 223 $content_source = PhabricatorContentSource::newForSource( 245 224 PhabricatorContentSource::SOURCE_DAEMON, 246 225 array()); 247 226 248 - $editor = id(new DifferentialTransactionEditor()) 249 - ->setActor($actor) 250 - ->setActingAsPHID($acting_as_phid) 251 - ->setContinueOnMissingFields(true) 252 - ->setContentSource($content_source) 253 - ->setChangedPriorToCommitURI($changed_uri) 254 - ->setIsCloseByCommit(true); 227 + $update_data = $extraction_engine->updateRevisionWithCommit( 228 + $revision, 229 + $commit, 230 + array( 231 + $commit_close_xaction, 232 + ), 233 + $content_source); 255 234 256 - try { 257 - $editor->applyTransactions($revision, $xactions); 258 - } catch (PhabricatorApplicationTransactionNoEffectException $ex) { 259 - // NOTE: We've marked transactions other than the CLOSE transaction 260 - // as ignored when they don't have an effect, so this means that we 261 - // lost a race to close the revision. That's perfectly fine, we can 262 - // just continue normally. 235 + foreach ($update_data as $key => $value) { 236 + $data->setCommitDetail($key, $value); 263 237 } 264 238 } 265 239 } ··· 278 252 279 253 $commit->writeImportStatusFlag( 280 254 PhabricatorRepositoryCommit::IMPORTED_MESSAGE); 281 - } 282 - 283 - 284 - private function loadChangedByCommit( 285 - DifferentialRevision $revision, 286 - DifferentialDiff $diff) { 287 - 288 - $repository = $this->repository; 289 - 290 - $vs_diff = id(new DifferentialDiffQuery()) 291 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 292 - ->withRevisionIDs(array($revision->getID())) 293 - ->needChangesets(true) 294 - ->setLimit(1) 295 - ->executeOne(); 296 - if (!$vs_diff) { 297 - return null; 298 - } 299 - 300 - if ($vs_diff->getCreationMethod() == 'commit') { 301 - return null; 302 - } 303 - 304 - $vs_changesets = array(); 305 - foreach ($vs_diff->getChangesets() as $changeset) { 306 - $path = $changeset->getAbsoluteRepositoryPath($repository, $vs_diff); 307 - $path = ltrim($path, '/'); 308 - $vs_changesets[$path] = $changeset; 309 - } 310 - 311 - $changesets = array(); 312 - foreach ($diff->getChangesets() as $changeset) { 313 - $path = $changeset->getAbsoluteRepositoryPath($repository, $diff); 314 - $path = ltrim($path, '/'); 315 - $changesets[$path] = $changeset; 316 - } 317 - 318 - if (array_fill_keys(array_keys($changesets), true) != 319 - array_fill_keys(array_keys($vs_changesets), true)) { 320 - return $vs_diff; 321 - } 322 - 323 - $file_phids = array(); 324 - foreach ($vs_changesets as $changeset) { 325 - $metadata = $changeset->getMetadata(); 326 - $file_phid = idx($metadata, 'new:binary-phid'); 327 - if ($file_phid) { 328 - $file_phids[$file_phid] = $file_phid; 329 - } 330 - } 331 - 332 - $files = array(); 333 - if ($file_phids) { 334 - $files = id(new PhabricatorFileQuery()) 335 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 336 - ->withPHIDs($file_phids) 337 - ->execute(); 338 - $files = mpull($files, null, 'getPHID'); 339 - } 340 - 341 - foreach ($changesets as $path => $changeset) { 342 - $vs_changeset = $vs_changesets[$path]; 343 - 344 - $file_phid = idx($vs_changeset->getMetadata(), 'new:binary-phid'); 345 - if ($file_phid) { 346 - if (!isset($files[$file_phid])) { 347 - return $vs_diff; 348 - } 349 - $drequest = DiffusionRequest::newFromDictionary(array( 350 - 'user' => PhabricatorUser::getOmnipotentUser(), 351 - 'repository' => $this->repository, 352 - 'commit' => $this->commit->getCommitIdentifier(), 353 - 'path' => $path, 354 - )); 355 - $corpus = DiffusionFileContentQuery::newFromDiffusionRequest($drequest) 356 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 357 - ->loadFileContent() 358 - ->getCorpus(); 359 - if ($files[$file_phid]->loadFileData() != $corpus) { 360 - return $vs_diff; 361 - } 362 - } else { 363 - $context = implode("\n", $changeset->makeChangesWithContext()); 364 - $vs_context = implode("\n", $vs_changeset->makeChangesWithContext()); 365 - 366 - // We couldn't just compare $context and $vs_context because following 367 - // diffs will be considered different: 368 - // 369 - // -(empty line) 370 - // -echo 'test'; 371 - // (empty line) 372 - // 373 - // (empty line) 374 - // -echo "test"; 375 - // -(empty line) 376 - 377 - $hunk = id(new DifferentialModernHunk())->setChanges($context); 378 - $vs_hunk = id(new DifferentialModernHunk())->setChanges($vs_context); 379 - if ($hunk->makeOldFile() != $vs_hunk->makeOldFile() || 380 - $hunk->makeNewFile() != $vs_hunk->makeNewFile()) { 381 - return $vs_diff; 382 - } 383 - } 384 - } 385 - 386 - return null; 387 255 } 388 256 389 257 private function resolveUserPHID(