diff --git a/src/applications/herald/controller/HeraldTestConsoleController.php b/src/applications/herald/controller/HeraldTestConsoleController.php --- a/src/applications/herald/controller/HeraldTestConsoleController.php +++ b/src/applications/herald/controller/HeraldTestConsoleController.php @@ -260,24 +260,48 @@ $query = PhabricatorApplicationTransactionQuery::newQueryForObject( $object); - $xactions = $query + $query ->withObjectPHIDs(array($object->getPHID())) - ->setViewer($viewer) - ->setLimit(100) - ->execute(); + ->setViewer($viewer); + + $xactions = new PhabricatorQueryIterator($query); $applied = array(); - // Pick the most recent group of transactions. This may not be exactly the - // same as what Herald acted on: for example, we may select a single group - // of transactions here which were really applied across two or more edits. - // Since this is relatively rare and we show you what we picked, it's okay - // that we just do roughly the right thing. + $recent_id = null; + $hard_limit = 1000; foreach ($xactions as $xaction) { - if (!$xaction->shouldDisplayGroupWith($applied)) { + $group_id = $xaction->getTransactionGroupID(); + + // If this is the first transaction, save the group ID: we want to + // select all transactions in the same group. + if (!$applied) { + $recent_id = $group_id; + if ($recent_id === null) { + // If the first transaction has no group ID, it is likely an older + // transaction from before the introduction of group IDs. In this + // case, select only the most recent transaction and bail out. + $applied[] = $xaction; + break; + } + } + + // If this transaction is from a different transaction group, we've + // found all the transactions applied in the most recent group. + if ($group_id !== $recent_id) { break; } + $applied[] = $xaction; + + if (count($applied) > $hard_limit) { + throw new Exception( + pht( + 'This object ("%s") has more than %s transactions in its most '. + 'recent transaction group; this is too many.', + $object->getPHID(), + new PhutilNumber($hard_limit))); + } } return $applied; diff --git a/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php b/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php --- a/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php +++ b/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php @@ -260,6 +260,13 @@ } } + $group_id = $xaction->getTransactionGroupID(); + if (!strlen($group_id)) { + $group_id = null; + } else { + $group_id = (string)$group_id; + } + $data[] = array( 'id' => (int)$xaction->getID(), 'phid' => (string)$xaction->getPHID(), @@ -268,6 +275,7 @@ 'objectPHID' => (string)$xaction->getObjectPHID(), 'dateCreated' => (int)$xaction->getDateCreated(), 'dateModified' => (int)$xaction->getDateModified(), + 'groupID' => $group_id, 'comments' => $comment_data, 'fields' => $fields, ); diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -1162,6 +1162,8 @@ throw $ex; } + $group_id = Filesystem::readRandomCharacters(32); + foreach ($xactions as $xaction) { if ($was_locked) { $is_override = $this->isLockOverrideTransaction($xaction); @@ -1171,6 +1173,8 @@ } $xaction->setObjectPHID($object->getPHID()); + $xaction->setTransactionGroupID($group_id); + if ($xaction->getComment()) { $xaction->setPHID($xaction->generatePHID()); $comment_editor->applyEdit($xaction, $xaction->getComment()); diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -177,6 +177,14 @@ return (bool)$this->getMetadataValue('core.lock-override', false); } + public function setTransactionGroupID($group_id) { + return $this->setMetadataValue('core.groupID', $group_id); + } + + public function getTransactionGroupID() { + return $this->getMetadataValue('core.groupID', null); + } + public function attachComment( PhabricatorApplicationTransactionComment $comment) { $this->comment = $comment;