Differential D16266 Diff 39131 src/applications/diffusion/conduit/DiffusionInternalGitRawDiffQueryConduitAPIMethod.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/diffusion/conduit/DiffusionInternalGitRawDiffQueryConduitAPIMethod.php
Show All 22 Lines | protected function defineCustomParamTypes() { | ||||
return array( | return array( | ||||
'commit' => 'required string', | 'commit' => 'required string', | ||||
); | ); | ||||
} | } | ||||
protected function getResult(ConduitAPIRequest $request) { | protected function getResult(ConduitAPIRequest $request) { | ||||
$drequest = $this->getDiffusionRequest(); | $drequest = $this->getDiffusionRequest(); | ||||
$repository = $drequest->getRepository(); | $repository = $drequest->getRepository(); | ||||
$commit = $request->getValue('commit'); | |||||
if (!$repository->isGit()) { | if (!$repository->isGit()) { | ||||
throw new Exception( | throw new Exception( | ||||
pht( | pht( | ||||
'This API method can only be called on Git repositories.')); | 'This API method can only be called on Git repositories.')); | ||||
} | } | ||||
// Check if the commit has parents. We're testing to see whether it is the | // Check if the commit has parents. We're testing to see whether it is the | ||||
// first commit in history (in which case we must use "git log") or some | // first commit in history (in which case we must use "git log") or some | ||||
// other commit (in which case we can use "git diff"). We'd rather use | // other commit (in which case we can use "git diff"). We'd rather use | ||||
// "git diff" because it has the right behavior for merge commits, but | // "git diff" because it has the right behavior for merge commits, but | ||||
// it requires the commit to have a parent that we can diff against. The | // it requires the commit to have a parent that we can diff against. The | ||||
// first commit doesn't, so "commit^" is not a valid ref. | // first commit doesn't, so "commit^" is not a valid ref. | ||||
list($parents) = $repository->execxLocalCommand( | list($parents) = $repository->execxLocalCommand( | ||||
'log -n1 --format=%s %s', | 'log -n1 --format=%s %s', | ||||
'%P', | '%P', | ||||
$request->getValue('commit')); | $commit); | ||||
$use_log = !strlen(trim($parents)); | $use_log = !strlen(trim($parents)); | ||||
// First, get a fast raw diff without "--find-copies-harder". This flag | |||||
// produces better results for moves and copies, but is explosively slow | |||||
// for large changes to large repositories. See T10423. | |||||
$raw = $this->getRawDiff($repository, $commit, $use_log, false); | |||||
// If we got a normal-sized diff (no more than 100 modified files), we'll | |||||
// try using "--find-copies-harder" to improve the output. This improved | |||||
// output is mostly useful for small changes anyway. | |||||
$try_harder = (substr_count($raw, "\n") <= 100); | |||||
if ($try_harder) { | |||||
try { | |||||
$raw = $this->getRawDiff($repository, $commit, $use_log, true); | |||||
} catch (Exception $ex) { | |||||
// Just ignore any exception we hit, we'll use the fast output | |||||
// instead. | |||||
} | |||||
} | |||||
return $raw; | |||||
} | |||||
private function getRawDiff( | |||||
PhabricatorRepository $repository, | |||||
$commit, | |||||
$use_log, | |||||
$try_harder) { | |||||
$flags = array( | |||||
'-n1', | |||||
'-M', | |||||
'-C', | |||||
'-B', | |||||
'--raw', | |||||
'-t', | |||||
'--abbrev=40', | |||||
); | |||||
if ($try_harder) { | |||||
$flags[] = '--find-copies-harder'; | |||||
} | |||||
if ($use_log) { | if ($use_log) { | ||||
// This is the first commit so we need to use "log". We know it's not a | // This is the first commit so we need to use "log". We know it's not a | ||||
// merge commit because it couldn't be merging anything, so this is safe. | // merge commit because it couldn't be merging anything, so this is safe. | ||||
// NOTE: "--pretty=format: " is to disable diff output, we only want the | // NOTE: "--pretty=format: " is to disable diff output, we only want the | ||||
// part we get from "--raw". | // part we get from "--raw". | ||||
list($raw) = $repository->execxLocalCommand( | $future = $repository->getLocalCommandFuture( | ||||
'log -n1 -M -C -B --find-copies-harder --raw -t '. | 'log %Ls --pretty=format: %s', | ||||
'--pretty=format: --abbrev=40 %s', | $flags, | ||||
$request->getValue('commit')); | $commit); | ||||
} else { | } else { | ||||
// Otherwise, we can use "diff", which will give us output for merges. | // Otherwise, we can use "diff", which will give us output for merges. | ||||
// We diff against the first parent, as this is generally the expectation | // We diff against the first parent, as this is generally the expectation | ||||
// and results in sensible behavior. | // and results in sensible behavior. | ||||
list($raw) = $repository->execxLocalCommand( | $future = $repository->getLocalCommandFuture( | ||||
'diff -n1 -M -C -B --find-copies-harder --raw -t '. | 'diff %Ls %s^1 %s', | ||||
'--abbrev=40 %s^1 %s', | $flags, | ||||
$request->getValue('commit'), | $commit, | ||||
$request->getValue('commit')); | $commit); | ||||
} | } | ||||
// Don't spend more than 30 seconds generating the slower output. | |||||
if ($try_harder) { | |||||
$future->setTimeout(30); | |||||
} | |||||
list($raw) = $future->resolvex(); | |||||
return $raw; | return $raw; | ||||
} | } | ||||
} | } |