@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

Fix an issue where non-ID changeset state keys were used as changeset IDs

Summary:
Ref T13519. This is a little fuzzy, but I think the workflow here is:

- View an intradiff, generating an ephemeral comparison changeset with no changeset ID. This produces a state key of "*".
- Apply "hidden" state changes to the changeset.
- View some other intradiff and/or diff view.
- The code attempts to use "*" as a changset ID?

I'm not entirely sure this is accurate; this was observed in production and I couldn't get a clean reproduction case locally.

Optimistically, try making changeset IDs explicit rather than relying on state keys to be "usually changeset-ID-like".

Test Plan: Used "hidden" locally across multiple intradiffs, but I wasn't cleanly able to reproduce the initial issue.

Maniphest Tasks: T13519

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

+14 -5
+5
src/applications/differential/storage/DifferentialViewState.php
··· 49 49 $properties['diffID'] = (int)$diff_id; 50 50 } 51 51 52 + $changeset_id = $changeset->getID(); 53 + if ($changeset_id !== null) { 54 + $properties['changesetID'] = (int)$changeset_id; 55 + } 56 + 52 57 $path_hash = $this->getChangesetPathHash($changeset); 53 58 $changeset_phid = $this->getChangesetKey($changeset); 54 59
+9 -5
src/infrastructure/diff/viewstate/PhabricatorChangesetViewStateEngine.php
··· 197 197 $entries = isort($entries, 'epoch'); 198 198 199 199 if ($entries) { 200 - $other_key = last_key($entries); 201 200 $other_spec = last($entries); 202 201 203 202 $this_version = (int)$changeset->getDiffID(); 204 203 $other_version = (int)idx($other_spec, 'diffID'); 205 204 $other_value = (bool)idx($other_spec, 'value', false); 205 + $other_id = (int)idx($other_spec, 'changesetID'); 206 206 207 207 if ($other_value === false) { 208 208 $is_hidden = false; ··· 211 211 } else { 212 212 $viewer = $this->getViewer(); 213 213 214 - $other_changeset = id(new DifferentialChangesetQuery()) 215 - ->setViewer($viewer) 216 - ->withIDs(array($other_key)) 217 - ->executeOne(); 214 + if ($other_id) { 215 + $other_changeset = id(new DifferentialChangesetQuery()) 216 + ->setViewer($viewer) 217 + ->withIDs(array($other_id)) 218 + ->executeOne(); 219 + } else { 220 + $other_changeset = null; 221 + } 218 222 219 223 $is_modified = false; 220 224 if ($other_changeset) {