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

Provide an alternate, more general "closeable" flag for commits

Summary:
Ref T4327. This provides a more general RefCursor-based way to identify closeable commits, and moves away from the messy `seenOnBranches` stuff. Basically:

- When a closeable ref (like the branch "master") is updated, query the VCS for all commits which are ancestors of the new ref, but not ancestors of the existing closeable heads we previously knew about. This is basically all the commits which have been merged or moved onto the closeable ref.
- Take these commits and set the "closeable" flag on the ones which don't have it yet, then queue new tasks to reprocess them.

I haven't removed the old stuff yet, but will do that shortly.

Test Plan:
- Ran `bin/repository discover` and `bin/repository refs` on a bunch of different VCSes and VCS states. The effects seemed correct (e.g., new commits were marked as closeable).

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T4327

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

+168 -4
+163 -4
src/applications/repository/engine/PhabricatorRepositoryRefEngine.php
··· 9 9 10 10 private $newRefs = array(); 11 11 private $deadRefs = array(); 12 + private $closeCommits = array(); 13 + private $hasNoCursors; 12 14 13 15 public function updateRefs() { 14 16 $this->newRefs = array(); 15 17 $this->deadRefs = array(); 18 + $this->closeCommits = array(); 16 19 17 20 $repository = $this->getRepository(); 18 21 ··· 50 53 ->execute(); 51 54 $cursor_groups = mgroup($all_cursors, 'getRefType'); 52 55 56 + $this->hasNoCursors = (!$all_cursors); 57 + 58 + // Find all the heads of closing refs. 59 + $all_closing_heads = array(); 60 + foreach ($all_cursors as $cursor) { 61 + if ($this->shouldCloseRef($cursor->getRefType(), $cursor->getRefName())) { 62 + $all_closing_heads[] = $cursor->getCommitIdentifier(); 63 + } 64 + } 65 + $all_closing_heads = array_unique($all_closing_heads); 66 + 53 67 foreach ($maps as $type => $refs) { 54 68 $cursor_group = idx($cursor_groups, $type, array()); 55 - $this->updateCursors($cursor_group, $refs, $type); 69 + $this->updateCursors($cursor_group, $refs, $type, $all_closing_heads); 70 + } 71 + 72 + if ($this->closeCommits) { 73 + $this->setCloseFlagOnCommits($this->closeCommits); 56 74 } 57 75 58 76 if ($this->newRefs || $this->deadRefs) { ··· 80 98 return $this; 81 99 } 82 100 101 + private function markCloseCommits(array $identifiers) { 102 + foreach ($identifiers as $identifier) { 103 + $this->closeCommits[$identifier] = $identifier; 104 + } 105 + return $this; 106 + } 107 + 83 108 private function updateCursors( 84 109 array $cursors, 85 110 array $new_refs, 86 - $ref_type) { 111 + $ref_type, 112 + array $all_closing_heads) { 87 113 $repository = $this->getRepository(); 88 114 89 115 // NOTE: Mercurial branches may have multiple branch heads; this logic ··· 149 175 ->setCommitIdentifier($identifier)); 150 176 } 151 177 152 - foreach ($added_commits as $identifier) { 153 - // TODO: Do autoclose stuff here. 178 + if ($this->shouldCloseRef($ref_type, $name)) { 179 + foreach ($added_commits as $identifier) { 180 + $new_identifiers = $this->loadNewCommitIdentifiers( 181 + $identifier, 182 + $all_closing_heads); 183 + 184 + $this->markCloseCommits($new_identifiers); 185 + } 154 186 } 155 187 } 156 188 ··· 167 199 foreach ($cursor_group as $cursor) { 168 200 $this->markRefDead($cursor); 169 201 } 202 + } 203 + } 204 + } 205 + 206 + private function shouldCloseRef($ref_type, $ref_name) { 207 + if ($ref_type !== PhabricatorRepositoryRefCursor::TYPE_BRANCH) { 208 + return false; 209 + } 210 + 211 + if ($this->hasNoCursors) { 212 + // If we don't have any cursors, don't close things. Particularly, this 213 + // corresponds to the case where you've just updated to this code on an 214 + // existing repository: we don't want to requeue message steps for every 215 + // commit on a closeable ref. 216 + return false; 217 + } 218 + 219 + return $this->getRepository()->shouldAutocloseBranch($ref_name); 220 + } 221 + 222 + /** 223 + * Find all ancestors of a new closing branch head which are not ancestors 224 + * of any old closing branch head. 225 + */ 226 + private function loadNewCommitIdentifiers( 227 + $new_head, 228 + array $all_closing_heads) { 229 + 230 + $repository = $this->getRepository(); 231 + $vcs = $repository->getVersionControlSystem(); 232 + switch ($vcs) { 233 + case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: 234 + if ($all_closing_heads) { 235 + $escheads = array(); 236 + foreach ($all_closing_heads as $head) { 237 + $escheads[] = hgsprintf('%s', $head); 238 + } 239 + $escheads = implode(' or ', $escheads); 240 + list($stdout) = $this->getRepository()->execxLocalCommand( 241 + 'log --template %s --rev %s', 242 + '{node}\n', 243 + hgsprintf('%s', $new_head).' - ('.$escheads.')'); 244 + } else { 245 + list($stdout) = $this->getRepository()->execxLocalCommand( 246 + 'log --template %s --rev %s', 247 + '{node}\n', 248 + hgsprintf('%s', $new_head)); 249 + } 250 + return phutil_split_lines($stdout, $retain_newlines = false); 251 + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: 252 + if ($all_closing_heads) { 253 + list($stdout) = $this->getRepository()->execxLocalCommand( 254 + 'log --format=%s %s --not %Ls', 255 + '%H', 256 + $new_head, 257 + $all_closing_heads); 258 + } else { 259 + list($stdout) = $this->getRepository()->execxLocalCommand( 260 + 'log --format=%s %s', 261 + '%H', 262 + $new_head); 263 + } 264 + return phutil_split_lines($stdout, $retain_newlines = false); 265 + default: 266 + throw new Exception(pht('Unsupported VCS "%s"!', $vcs)); 267 + } 268 + } 269 + 270 + /** 271 + * Mark a list of commits as closeable, and queue workers for those commits 272 + * which don't already have the flag. 273 + */ 274 + private function setCloseFlagOnCommits(array $identifiers) { 275 + $repository = $this->getRepository(); 276 + $commit_table = new PhabricatorRepositoryCommit(); 277 + $conn_w = $commit_table->establishConnection('w'); 278 + 279 + $vcs = $repository->getVersionControlSystem(); 280 + switch ($vcs) { 281 + case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: 282 + $class = 'PhabricatorRepositoryGitCommitMessageParserWorker'; 283 + break; 284 + case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: 285 + $class = 'PhabricatorRepositorySvnCommitMessageParserWorker'; 286 + break; 287 + case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: 288 + $class = 'PhabricatorRepositoryMercurialCommitMessageParserWorker'; 289 + break; 290 + default: 291 + throw new Exception("Unknown repository type '{$vcs}'!"); 292 + } 293 + 294 + $all_commits = queryfx_all( 295 + $conn_w, 296 + 'SELECT id, commitIdentifier, importStatus FROM %T 297 + WHERE commitIdentifier IN (%Ls)', 298 + $commit_table->getTableName(), 299 + $identifiers); 300 + 301 + $closeable_flag = PhabricatorRepositoryCommit::IMPORTED_CLOSEABLE; 302 + 303 + $all_commits = ipull($all_commits, null, 'commitIdentifier'); 304 + foreach ($identifiers as $identifier) { 305 + $row = idx($all_commits, $identifier); 306 + 307 + if (!$row) { 308 + throw new Exception( 309 + pht( 310 + 'Commit "%s" has not been discovered yet! Run discovery before '. 311 + 'updating refs.', 312 + $identifier)); 313 + } 314 + 315 + if (!($row['importStatus'] & $closeable_flag)) { 316 + queryfx( 317 + $conn_w, 318 + 'UPDATE %T SET importStatus = (importStatus | %d) WHERE id = %d', 319 + $commit_table->getTableName(), 320 + $closeable_flag, 321 + $row['id']); 322 + 323 + $data = array( 324 + 'commitID' => $row['id'], 325 + 'only' => true, 326 + ); 327 + 328 + PhabricatorWorker::scheduleTask($class, $data); 170 329 } 171 330 } 172 331 }
+5
src/applications/repository/storage/PhabricatorRepository.php
··· 552 552 throw new Exception("Unrecognized version control system."); 553 553 } 554 554 555 + $closeable_flag = PhabricatorRepositoryCommit::IMPORTED_CLOSEABLE; 556 + if ($commit->isPartiallyImported($closeable_flag)) { 557 + return true; 558 + } 559 + 555 560 $branches = $data->getCommitDetail('seenOnBranches', array()); 556 561 foreach ($branches as $branch) { 557 562 if ($this->shouldAutocloseBranch($branch)) {