@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

Allow revisions to revert commits and one another, and commits to revert revisions

Summary:
Ref T13057. This makes "reverts" syntax more visible and useful. In particular, you can now `Reverts Dxx` in a revision or commit, and `Reverts <hash>` from a revision.

When you do, the corresponding object will get a more-visible cross-reference marker in its timeline:

{F5405517}

From here, we can look at surfacing revert information more heavily, since we can now query it on revision/commit pages via edges.

Test Plan: Used "reverts <hash>" and "reverts <revision>" in Differential and Diffusion, got sensible results in the timeline.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13057

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

+149 -74
+51 -5
src/applications/audit/editor/PhabricatorAuditEditor.php
··· 358 358 array $changes, 359 359 PhutilMarkupEngine $engine) { 360 360 361 - // we are only really trying to find unmentionable phids here... 362 - // don't bother with this outside initial commit (i.e. create) 363 - // transaction 361 + $actor = $this->getActor(); 362 + $result = array(); 363 + 364 + // Some interactions (like "Fixes Txxx" interacting with Maniphest) have 365 + // already been processed, so we're only re-parsing them here to avoid 366 + // generating an extra redundant mention. Other interactions are being 367 + // processed for the first time. 368 + 369 + // We're only recognizing magic in the commit message itself, not in 370 + // audit comments. 371 + 364 372 $is_commit = false; 365 373 foreach ($xactions as $xaction) { 366 374 switch ($xaction->getTransactionType()) { ··· 370 378 } 371 379 } 372 380 373 - // "result" is always an array.... 374 - $result = array(); 375 381 if (!$is_commit) { 376 382 return $result; 377 383 } ··· 403 409 ->withNames($monograms) 404 410 ->execute(); 405 411 $phid_map[] = mpull($objects, 'getPHID', 'getPHID'); 412 + 413 + 414 + $reverts_refs = id(new DifferentialCustomFieldRevertsParser()) 415 + ->parseCorpus($huge_block); 416 + $reverts = array_mergev(ipull($reverts_refs, 'monograms')); 417 + if ($reverts) { 418 + // Only allow commits to revert other commits in the same repository. 419 + $reverted_commits = id(new DiffusionCommitQuery()) 420 + ->setViewer($actor) 421 + ->withRepository($object->getRepository()) 422 + ->withIdentifiers($reverts) 423 + ->execute(); 424 + 425 + $reverted_revisions = id(new PhabricatorObjectQuery()) 426 + ->setViewer($actor) 427 + ->withNames($reverts) 428 + ->withTypes( 429 + array( 430 + DifferentialRevisionPHIDType::TYPECONST, 431 + )) 432 + ->execute(); 433 + 434 + $reverted_phids = 435 + mpull($reverted_commits, 'getPHID', 'getPHID') + 436 + mpull($reverted_revisions, 'getPHID', 'getPHID'); 437 + 438 + // NOTE: Skip any write attempts if a user cleverly implies a commit 439 + // reverts itself, although this would be exceptionally clever in Git 440 + // or Mercurial. 441 + unset($reverted_phids[$object->getPHID()]); 442 + 443 + $reverts_edge = DiffusionCommitRevertsCommitEdgeType::EDGECONST; 444 + $result[] = id(new PhabricatorAuditTransaction()) 445 + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 446 + ->setMetadataValue('edge:type', $reverts_edge) 447 + ->setNewValue(array('+' => $reverted_phids)); 448 + 449 + $phid_map[] = $reverted_phids; 450 + } 451 + 406 452 $phid_map = array_mergev($phid_map); 407 453 $this->setUnmentionablePHIDMap($phid_map); 408 454
+38 -1
src/applications/differential/editor/DifferentialTransactionEditor.php
··· 919 919 } 920 920 } 921 921 922 - $this->setUnmentionablePHIDMap(array_merge($task_phids, $rev_phids)); 922 + $revert_refs = id(new DifferentialCustomFieldRevertsParser()) 923 + ->parseCorpus($content_block); 924 + 925 + $revert_monograms = array(); 926 + foreach ($revert_refs as $match) { 927 + foreach ($match['monograms'] as $monogram) { 928 + $revert_monograms[] = $monogram; 929 + } 930 + } 931 + 932 + if ($revert_monograms) { 933 + $revert_objects = id(new PhabricatorObjectQuery()) 934 + ->setViewer($this->getActor()) 935 + ->withNames($revert_monograms) 936 + ->withTypes( 937 + array( 938 + DifferentialRevisionPHIDType::TYPECONST, 939 + PhabricatorRepositoryCommitPHIDType::TYPECONST, 940 + )) 941 + ->execute(); 942 + 943 + $revert_phids = mpull($revert_objects, 'getPHID', 'getPHID'); 944 + 945 + // Don't let an object revert itself, although other silly stuff like 946 + // cycles of objects reverting each other is not prevented. 947 + unset($revert_phids[$object->getPHID()]); 948 + 949 + $revert_type = DiffusionCommitRevertsCommitEdgeType::EDGECONST; 950 + $edges[$revert_type] = $revert_phids; 951 + } else { 952 + $revert_phids = array(); 953 + } 954 + 955 + $this->setUnmentionablePHIDMap( 956 + array_merge( 957 + $task_phids, 958 + $rev_phids, 959 + $revert_phids)); 923 960 924 961 $result = array(); 925 962 foreach ($edges as $type => $specs) {
+6 -6
src/applications/diffusion/edge/DiffusionCommitRevertedByCommitEdgeType.php
··· 19 19 $add_edges) { 20 20 21 21 return pht( 22 - '%s added %s reverting commit(s): %s.', 22 + '%s added %s reverting change(s): %s.', 23 23 $actor, 24 24 $add_count, 25 25 $add_edges); ··· 31 31 $rem_edges) { 32 32 33 33 return pht( 34 - '%s removed %s reverting commit(s): %s.', 34 + '%s removed %s reverting change(s): %s.', 35 35 $actor, 36 36 $rem_count, 37 37 $rem_edges); ··· 46 46 $rem_edges) { 47 47 48 48 return pht( 49 - '%s edited reverting commit(s), added %s: %s; removed %s: %s.', 49 + '%s edited reverting change(s), added %s: %s; removed %s: %s.', 50 50 $actor, 51 51 $add_count, 52 52 $add_edges, ··· 61 61 $add_edges) { 62 62 63 63 return pht( 64 - '%s added %s reverting commit(s) for %s: %s.', 64 + '%s added %s reverting change(s) for %s: %s.', 65 65 $actor, 66 66 $add_count, 67 67 $object, ··· 75 75 $rem_edges) { 76 76 77 77 return pht( 78 - '%s removed %s reverting commit(s) for %s: %s.', 78 + '%s removed %s reverting change(s) for %s: %s.', 79 79 $actor, 80 80 $rem_count, 81 81 $object, ··· 92 92 $rem_edges) { 93 93 94 94 return pht( 95 - '%s edited reverting commit(s) for %s, added %s: %s; removed %s: %s.', 95 + '%s edited reverting change(s) for %s, added %s: %s; removed %s: %s.', 96 96 $actor, 97 97 $object, 98 98 $add_count,
+6 -6
src/applications/diffusion/edge/DiffusionCommitRevertsCommitEdgeType.php
··· 22 22 $add_edges) { 23 23 24 24 return pht( 25 - '%s added %s reverted commit(s): %s.', 25 + '%s added %s reverted change(s): %s.', 26 26 $actor, 27 27 $add_count, 28 28 $add_edges); ··· 34 34 $rem_edges) { 35 35 36 36 return pht( 37 - '%s removed %s reverted commit(s): %s.', 37 + '%s removed %s reverted change(s): %s.', 38 38 $actor, 39 39 $rem_count, 40 40 $rem_edges); ··· 49 49 $rem_edges) { 50 50 51 51 return pht( 52 - '%s edited reverted commit(s), added %s: %s; removed %s: %s.', 52 + '%s edited reverted change(s), added %s: %s; removed %s: %s.', 53 53 $actor, 54 54 $add_count, 55 55 $add_edges, ··· 64 64 $add_edges) { 65 65 66 66 return pht( 67 - '%s added %s reverted commit(s) for %s: %s.', 67 + '%s added %s reverted change(s) for %s: %s.', 68 68 $actor, 69 69 $add_count, 70 70 $object, ··· 78 78 $rem_edges) { 79 79 80 80 return pht( 81 - '%s removed %s reverted commit(s) for %s: %s.', 81 + '%s removed %s reverted change(s) for %s: %s.', 82 82 $actor, 83 83 $rem_count, 84 84 $object, ··· 95 95 $rem_edges) { 96 96 97 97 return pht( 98 - '%s edited reverted commit(s) for %s, added %s: %s; removed %s: %s.', 98 + '%s edited reverted change(s) for %s, added %s: %s; removed %s: %s.', 99 99 $actor, 100 100 $object, 101 101 $add_count,
+2 -24
src/applications/repository/worker/PhabricatorRepositoryCommitHeraldWorker.php
··· 15 15 protected function parseCommit( 16 16 PhabricatorRepository $repository, 17 17 PhabricatorRepositoryCommit $commit) { 18 + $viewer = PhabricatorUser::getOmnipotentUser(); 18 19 19 20 if ($this->shouldSkipImportStep()) { 20 21 // This worker has no followup tasks, so we can just bail out ··· 50 51 id(new PhabricatorDiffusionApplication())->getPHID()); 51 52 52 53 $editor = id(new PhabricatorAuditEditor()) 53 - ->setActor(PhabricatorUser::getOmnipotentUser()) 54 + ->setActor($viewer) 54 55 ->setActingAsPHID($acting_as_phid) 55 56 ->setContinueOnMissingFields(true) 56 57 ->setContinueOnNoEffect(true) ··· 68 69 'committerName' => $data->getCommitDetail('committer'), 69 70 'committerPHID' => $data->getCommitDetail('committerPHID'), 70 71 )); 71 - 72 - $reverts_refs = id(new DifferentialCustomFieldRevertsParser()) 73 - ->parseCorpus($data->getCommitMessage()); 74 - $reverts = array_mergev(ipull($reverts_refs, 'monograms')); 75 - 76 - if ($reverts) { 77 - $reverted_commits = id(new DiffusionCommitQuery()) 78 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 79 - ->withRepository($repository) 80 - ->withIdentifiers($reverts) 81 - ->execute(); 82 - $reverted_commit_phids = mpull($reverted_commits, 'getPHID', 'getPHID'); 83 - 84 - // NOTE: Skip any write attempts if a user cleverly implies a commit 85 - // reverts itself. 86 - unset($reverted_commit_phids[$commit->getPHID()]); 87 - 88 - $reverts_edge = DiffusionCommitRevertsCommitEdgeType::EDGECONST; 89 - $xactions[] = id(new PhabricatorAuditTransaction()) 90 - ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 91 - ->setMetadataValue('edge:type', $reverts_edge) 92 - ->setNewValue(array('+' => array_fuse($reverted_commit_phids))); 93 - } 94 72 95 73 try { 96 74 $raw_patch = $this->loadRawPatchText($repository, $commit);
+14
src/applications/transactions/storage/PhabricatorApplicationTransaction.php
··· 458 458 case PhabricatorTransactions::TYPE_JOIN_POLICY: 459 459 return 'fa-lock'; 460 460 case PhabricatorTransactions::TYPE_EDGE: 461 + switch ($this->getMetadataValue('edge:type')) { 462 + case DiffusionCommitRevertedByCommitEdgeType::EDGECONST: 463 + return 'fa-undo'; 464 + case DiffusionCommitRevertsCommitEdgeType::EDGECONST: 465 + return 'fa-ambulance'; 466 + } 461 467 return 'fa-link'; 462 468 case PhabricatorTransactions::TYPE_BUILDABLE: 463 469 return 'fa-wrench'; ··· 494 500 $comment = $this->getComment(); 495 501 if ($comment && $comment->getIsRemoved()) { 496 502 return 'black'; 503 + } 504 + break; 505 + case PhabricatorTransactions::TYPE_EDGE: 506 + switch ($this->getMetadataValue('edge:type')) { 507 + case DiffusionCommitRevertedByCommitEdgeType::EDGECONST: 508 + return 'pink'; 509 + case DiffusionCommitRevertsCommitEdgeType::EDGECONST: 510 + return 'sky'; 497 511 } 498 512 break; 499 513 case PhabricatorTransactions::TYPE_BUILDABLE:
+32 -32
src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
··· 676 676 '%s edited commit(s), added %s: %s; removed %s: %s.' => 677 677 '%s edited commits, added %3$s; removed %5$s.', 678 678 679 - '%s added %s reverted commit(s): %s.' => array( 679 + '%s added %s reverted change(s): %s.' => array( 680 680 array( 681 - '%s added a reverted commit: %3$s.', 682 - '%s added reverted commits: %3$s.', 681 + '%s added a reverted change: %3$s.', 682 + '%s added reverted changes: %3$s.', 683 683 ), 684 684 ), 685 685 686 - '%s removed %s reverted commit(s): %s.' => array( 686 + '%s removed %s reverted change(s): %s.' => array( 687 687 array( 688 - '%s removed a reverted commit: %3$s.', 689 - '%s removed reverted commits: %3$s.', 688 + '%s removed a reverted change: %3$s.', 689 + '%s removed reverted changes: %3$s.', 690 690 ), 691 691 ), 692 692 693 - '%s edited reverted commit(s), added %s: %s; removed %s: %s.' => 694 - '%s edited reverted commits, added %3$s; removed %5$s.', 693 + '%s edited reverted change(s), added %s: %s; removed %s: %s.' => 694 + '%s edited reverted changes, added %3$s; removed %5$s.', 695 695 696 - '%s added %s reverted commit(s) for %s: %s.' => array( 696 + '%s added %s reverted change(s) for %s: %s.' => array( 697 697 array( 698 - '%s added a reverted commit for %3$s: %4$s.', 699 - '%s added reverted commits for %3$s: %4$s.', 698 + '%s added a reverted change for %3$s: %4$s.', 699 + '%s added reverted changes for %3$s: %4$s.', 700 700 ), 701 701 ), 702 702 703 - '%s removed %s reverted commit(s) for %s: %s.' => array( 703 + '%s removed %s reverted change(s) for %s: %s.' => array( 704 704 array( 705 - '%s removed a reverted commit for %3$s: %4$s.', 706 - '%s removed reverted commits for %3$s: %4$s.', 705 + '%s removed a reverted change for %3$s: %4$s.', 706 + '%s removed reverted changes for %3$s: %4$s.', 707 707 ), 708 708 ), 709 709 710 - '%s edited reverted commit(s) for %s, added %s: %s; removed %s: %s.' => 711 - '%s edited reverted commits for %2$s, added %4$s; removed %6$s.', 710 + '%s edited reverted change(s) for %s, added %s: %s; removed %s: %s.' => 711 + '%s edited reverted changes for %2$s, added %4$s; removed %6$s.', 712 712 713 - '%s added %s reverting commit(s): %s.' => array( 713 + '%s added %s reverting change(s): %s.' => array( 714 714 array( 715 - '%s added a reverting commit: %3$s.', 716 - '%s added reverting commits: %3$s.', 715 + '%s added a reverting change: %3$s.', 716 + '%s added reverting changes: %3$s.', 717 717 ), 718 718 ), 719 719 720 - '%s removed %s reverting commit(s): %s.' => array( 720 + '%s removed %s reverting change(s): %s.' => array( 721 721 array( 722 - '%s removed a reverting commit: %3$s.', 723 - '%s removed reverting commits: %3$s.', 722 + '%s removed a reverting change: %3$s.', 723 + '%s removed reverting changes: %3$s.', 724 724 ), 725 725 ), 726 726 727 - '%s edited reverting commit(s), added %s: %s; removed %s: %s.' => 728 - '%s edited reverting commits, added %3$s; removed %5$s.', 727 + '%s edited reverting change(s), added %s: %s; removed %s: %s.' => 728 + '%s edited reverting changes, added %3$s; removed %5$s.', 729 729 730 - '%s added %s reverting commit(s) for %s: %s.' => array( 730 + '%s added %s reverting change(s) for %s: %s.' => array( 731 731 array( 732 - '%s added a reverting commit for %3$s: %4$s.', 733 - '%s added reverting commits for %3$s: %4$s.', 732 + '%s added a reverting change for %3$s: %4$s.', 733 + '%s added reverting changes for %3$s: %4$s.', 734 734 ), 735 735 ), 736 736 737 - '%s removed %s reverting commit(s) for %s: %s.' => array( 737 + '%s removed %s reverting change(s) for %s: %s.' => array( 738 738 array( 739 - '%s removed a reverting commit for %3$s: %4$s.', 740 - '%s removed reverting commits for %3$s: %4$s.', 739 + '%s removed a reverting change for %3$s: %4$s.', 740 + '%s removed reverting changes for %3$s: %4$s.', 741 741 ), 742 742 ), 743 743 744 - '%s edited reverting commit(s) for %s, added %s: %s; removed %s: %s.' => 745 - '%s edited reverting commits for %s, added %4$s; removed %6$s.', 744 + '%s edited reverting change(s) for %s, added %s: %s; removed %s: %s.' => 745 + '%s edited reverting changes for %s, added %4$s; removed %6$s.', 746 746 747 747 '%s changed project member(s), added %d: %s; removed %d: %s.' => 748 748 '%s changed project members, added %3$s; removed %5$s.',