@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
at recaptime-dev/main 123 lines 3.7 kB view raw
1<?php 2 3final class DiffusionInternalGitRawDiffQueryConduitAPIMethod 4 extends DiffusionQueryConduitAPIMethod { 5 6 public function isInternalAPI() { 7 return true; 8 } 9 10 public function getAPIMethodName() { 11 return 'diffusion.internal.gitrawdiffquery'; 12 } 13 14 public function getMethodDescription() { 15 return pht('Internal method for getting raw diff information.'); 16 } 17 18 protected function defineReturnType() { 19 return 'string'; 20 } 21 22 protected function defineCustomParamTypes() { 23 return array( 24 'commit' => 'required string', 25 ); 26 } 27 28 protected function getResult(ConduitAPIRequest $request) { 29 $drequest = $this->getDiffusionRequest(); 30 $repository = $drequest->getRepository(); 31 $commit = $request->getValue('commit'); 32 33 if (!$repository->isGit()) { 34 throw new Exception( 35 pht( 36 'This API method can only be called on Git repositories.')); 37 } 38 39 // Check if the commit has parents. We're testing to see whether it is the 40 // first commit in history (in which case we must use "git log") or some 41 // other commit (in which case we can use "git diff"). We'd rather use 42 // "git diff" because it has the right behavior for merge commits, but 43 // it requires the commit to have a parent that we can diff against. The 44 // first commit doesn't, so "commit^" is not a valid ref. 45 list($parents) = $repository->execxLocalCommand( 46 'log -n1 %s %s --', 47 '--format=%P', 48 gitsprintf('%s', $commit)); 49 $use_log = !strlen(trim($parents)); 50 51 // First, get a fast raw diff without "--find-copies-harder". This flag 52 // produces better results for moves and copies, but is explosively slow 53 // for large changes to large repositories. See T10423. 54 $raw = $this->getRawDiff($repository, $commit, $use_log, false); 55 56 // If we got a normal-sized diff (no more than 100 modified files), we'll 57 // try using "--find-copies-harder" to improve the output. This improved 58 // output is mostly useful for small changes anyway. 59 $try_harder = (substr_count($raw, "\n") <= 100); 60 if ($try_harder) { 61 try { 62 $raw = $this->getRawDiff($repository, $commit, $use_log, true); 63 } catch (Exception $ex) { 64 // Just ignore any exception we hit, we'll use the fast output 65 // instead. 66 } 67 } 68 69 return $raw; 70 } 71 72 private function getRawDiff( 73 PhabricatorRepository $repository, 74 $commit, 75 $use_log, 76 $try_harder) { 77 78 $flags = array( 79 '-n1', 80 '-M', 81 '-C', 82 '-B', 83 '--raw', 84 '-t', 85 '--abbrev=40', 86 ); 87 88 if ($try_harder) { 89 $flags[] = '--find-copies-harder'; 90 } 91 92 if ($use_log) { 93 // This is the first commit so we need to use "log". We know it's not a 94 // merge commit because it couldn't be merging anything, so this is safe. 95 96 // NOTE: "--pretty=format: " is to disable diff output, we only want the 97 // part we get from "--raw". 98 $future = $repository->getLocalCommandFuture( 99 'log %Ls --pretty=format: %s --', 100 $flags, 101 gitsprintf('%s', $commit)); 102 } else { 103 // Otherwise, we can use "diff", which will give us output for merges. 104 // We diff against the first parent, as this is generally the expectation 105 // and results in sensible behavior. 106 $future = $repository->getLocalCommandFuture( 107 'diff %Ls %s %s --', 108 $flags, 109 gitsprintf('%s^1', $commit), 110 gitsprintf('%s', $commit)); 111 } 112 113 // Don't spend more than 30 seconds generating the slower output. 114 if ($try_harder) { 115 $future->setTimeout(30); 116 } 117 118 list($raw) = $future->resolvex(); 119 120 return $raw; 121 } 122 123}