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

Fix an issue where ancestors of permanent refs might not be published during import or if a branch is later made permanent

Summary:
Fixes T13284. See that task for substantial discussion. There are currently two cases where we'll skip over commits which we should publish:

- if a branch is not permanent, then later made permanent; or
- in some cases, the first time we examine branches in a repository.

In both cases, this error is one-shot and things work correctly going forward. The root cause is conflation between the states "this ref currently permanent" and "this ref was permanent the last time we updated refs".

Separate these pieces of state and cover all these cases. Also introduce a "--rebuild" flag to fix the state of bad commits.

Test Plan:
See T13284 for the three major cases:

- initial import;
- push changes to a nonpermanent branch, update, then make it permanent;
- push chanegs to a nonpermanent branch, update, push more changes, then make it permanent.

Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam

Maniphest Tasks: T13284

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

+109 -29
+2
resources/sql/autopatches/20190924.diffusion.01.permanent.sql
··· 1 + ALTER TABLE {$NAMESPACE}_repository.repository_refcursor 2 + ADD isPermanent BOOL NOT NULL;
+2
resources/sql/autopatches/20190924.diffusion.02.default.sql
··· 1 + UPDATE {$NAMESPACE}_repository.repository_refcursor 2 + SET isPermanent = 1;
+9 -7
src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php
··· 195 195 196 196 $alternate = null; 197 197 if ($type == 'tag') { 198 - $alternate = $identifier; 199 - $identifier = idx($tag_map, $ref); 200 - if (!$identifier) { 201 - throw new Exception( 202 - pht( 203 - "Failed to look up tag '%s'!", 204 - $ref)); 198 + $tag_identifier = idx($tag_map, $ref); 199 + if ($tag_identifier === null) { 200 + // This can happen when we're asked to resolve the hash of a "tag" 201 + // object created with "git tag --annotate" that isn't currently 202 + // reachable from any ref. Just leave things as they are. 203 + } else { 204 + // Otherwise, we have a normal named tag. 205 + $alternate = $identifier; 206 + $identifier = $tag_identifier; 205 207 } 206 208 } 207 209
+87 -22
src/applications/repository/engine/PhabricatorRepositoryRefEngine.php
··· 10 10 private $newPositions = array(); 11 11 private $deadPositions = array(); 12 12 private $closeCommits = array(); 13 - private $hasNoCursors; 13 + private $rebuild; 14 + 15 + public function setRebuild($rebuild) { 16 + $this->rebuild = $rebuild; 17 + return $this; 18 + } 19 + 20 + public function getRebuild() { 21 + return $this->rebuild; 22 + } 14 23 15 24 public function updateRefs() { 16 25 $this->newPositions = array(); ··· 60 69 ->execute(); 61 70 $cursor_groups = mgroup($all_cursors, 'getRefType'); 62 71 63 - $this->hasNoCursors = (!$all_cursors); 64 - 65 - // Find all the heads of closing refs. 72 + // Find all the heads of permanent refs. 66 73 $all_closing_heads = array(); 67 74 foreach ($all_cursors as $cursor) { 68 - $should_close = $this->shouldCloseRef( 69 - $cursor->getRefType(), 70 - $cursor->getRefName()); 71 - if (!$should_close) { 75 + 76 + // See T13284. Note that we're considering whether this ref was a 77 + // permanent ref or not the last time we updated refs for this 78 + // repository. This allows us to handle things properly when a ref 79 + // is reconfigured from non-permanent to permanent. 80 + 81 + $was_permanent = $cursor->getIsPermanent(); 82 + if (!$was_permanent) { 72 83 continue; 73 84 } 74 85 ··· 76 87 $all_closing_heads[] = $identifier; 77 88 } 78 89 } 90 + 79 91 $all_closing_heads = array_unique($all_closing_heads); 80 92 $all_closing_heads = $this->removeMissingCommits($all_closing_heads); 81 93 ··· 88 100 $this->setCloseFlagOnCommits($this->closeCommits); 89 101 } 90 102 91 - if ($this->newPositions || $this->deadPositions) { 103 + $save_cursors = $this->getCursorsForUpdate($all_cursors); 104 + 105 + if ($this->newPositions || $this->deadPositions || $save_cursors) { 92 106 $repository->openTransaction(); 93 107 94 108 $this->saveNewPositions(); 95 109 $this->deleteDeadPositions(); 96 110 111 + foreach ($save_cursors as $cursor) { 112 + $cursor->save(); 113 + } 114 + 97 115 $repository->saveTransaction(); 98 116 } 99 117 ··· 103 121 } 104 122 } 105 123 124 + private function getCursorsForUpdate(array $cursors) { 125 + assert_instances_of($cursors, 'PhabricatorRepositoryRefCursor'); 126 + 127 + $results = array(); 128 + 129 + foreach ($cursors as $cursor) { 130 + $ref_type = $cursor->getRefType(); 131 + $ref_name = $cursor->getRefName(); 132 + 133 + $is_permanent = $this->isPermanentRef($ref_type, $ref_name); 134 + 135 + if ($is_permanent == $cursor->getIsPermanent()) { 136 + continue; 137 + } 138 + 139 + $cursor->setIsPermanent((int)$is_permanent); 140 + $results[] = $cursor; 141 + } 142 + 143 + return $results; 144 + } 145 + 106 146 private function updateBranchStates( 107 147 PhabricatorRepository $repository, 108 148 array $branches) { ··· 301 341 $this->markPositionNew($new_position); 302 342 } 303 343 304 - if ($this->shouldCloseRef($ref_type, $name)) { 305 - foreach ($added_commits as $identifier) { 344 + if ($this->isPermanentRef($ref_type, $name)) { 345 + 346 + // See T13284. If this cursor was already marked as permanent, we 347 + // only need to publish the newly created ref positions. However, if 348 + // this cursor was not previously permanent but has become permanent, 349 + // we need to publish all the ref positions. 350 + 351 + // This corresponds to users reconfiguring a branch to make it 352 + // permanent without pushing any new commits to it. 353 + 354 + $is_rebuild = $this->getRebuild(); 355 + $was_permanent = $ref_cursor->getIsPermanent(); 356 + 357 + if ($is_rebuild || !$was_permanent) { 358 + $update_all = true; 359 + } else { 360 + $update_all = false; 361 + } 362 + 363 + if ($update_all) { 364 + $update_commits = $new_commits; 365 + } else { 366 + $update_commits = $added_commits; 367 + } 368 + 369 + if ($is_rebuild) { 370 + $exclude = array(); 371 + } else { 372 + $exclude = $all_closing_heads; 373 + } 374 + 375 + foreach ($update_commits as $identifier) { 306 376 $new_identifiers = $this->loadNewCommitIdentifiers( 307 377 $identifier, 308 - $all_closing_heads); 378 + $exclude); 309 379 310 380 $this->markCloseCommits($new_identifiers); 311 381 } ··· 334 404 } 335 405 } 336 406 337 - private function shouldCloseRef($ref_type, $ref_name) { 407 + private function isPermanentRef($ref_type, $ref_name) { 338 408 if ($ref_type !== PhabricatorRepositoryRefCursor::TYPE_BRANCH) { 339 - return false; 340 - } 341 - 342 - if ($this->hasNoCursors) { 343 - // If we don't have any cursors, don't close things. Particularly, this 344 - // corresponds to the case where you've just updated to this code on an 345 - // existing repository: we don't want to requeue message steps for every 346 - // commit on a closeable ref. 347 409 return false; 348 410 } 349 411 ··· 505 567 $ref_type, 506 568 $ref_name) { 507 569 570 + $is_permanent = $this->isPermanentRef($ref_type, $ref_name); 571 + 508 572 $cursor = id(new PhabricatorRepositoryRefCursor()) 509 573 ->setRepositoryPHID($repository->getPHID()) 510 574 ->setRefType($ref_type) 511 - ->setRefName($ref_name); 575 + ->setRefName($ref_name) 576 + ->setIsPermanent((int)$is_permanent); 512 577 513 578 try { 514 579 return $cursor->save();
+7
src/applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php
··· 15 15 'help' => pht('Show additional debugging information.'), 16 16 ), 17 17 array( 18 + 'name' => 'rebuild', 19 + 'help' => pht( 20 + 'Publish commits currently reachable from any permanent ref, '. 21 + 'ignoring the cached ref state.'), 22 + ), 23 + array( 18 24 'name' => 'repos', 19 25 'wildcard' => true, 20 26 ), ··· 41 47 $engine = id(new PhabricatorRepositoryRefEngine()) 42 48 ->setRepository($repo) 43 49 ->setVerbose($args->getArg('verbose')) 50 + ->setRebuild($args->getArg('rebuild')) 44 51 ->updateRefs(); 45 52 } 46 53
+2
src/applications/repository/storage/PhabricatorRepositoryRefCursor.php
··· 19 19 protected $refNameHash; 20 20 protected $refNameRaw; 21 21 protected $refNameEncoding; 22 + protected $isPermanent; 22 23 23 24 private $repository = self::ATTACHABLE; 24 25 private $positions = self::ATTACHABLE; ··· 34 35 'refType' => 'text32', 35 36 'refNameHash' => 'bytes12', 36 37 'refNameEncoding' => 'text16?', 38 + 'isPermanent' => 'bool', 37 39 ), 38 40 self::CONFIG_KEY_SCHEMA => array( 39 41 'key_ref' => array(