1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-20 05:42:40 +01:00

Do the heavy lifting for git commit hooks

Summary:
Ref T4189. This doesn't add any rules yet, but does all the heavy lifting to figure out what's changed and put it in a consuamble (if somewhat ad-hoc) datastructure, which lists all the ref and tag modifications and all the new commits in a consistent way.

From here, it should be fairly straightforward to add top-level rules (e.g., ff pushes only).

Test Plan: Output is huge, see comments.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T4189

Differential Revision: https://secure.phabricator.com/D7687
This commit is contained in:
epriestley 2013-12-03 10:27:45 -08:00
parent 9942c2a39e
commit 632e1ceda6

View file

@ -61,14 +61,128 @@ final class DiffusionCommitHookEngine extends Phobject {
return $err; return $err;
} }
/**
* @task git
*/
private function executeGitHook() { private function executeGitHook() {
$updates = $this->parseGitUpdates($this->getStdin()); $updates = $this->parseGitUpdates($this->getStdin());
// TODO: Do useful things. // TODO: Do cheap checks: non-ff commits, mutating refs without access,
// creating or deleting things you can't touch. We can do all non-content
// checks here.
$updates = $this->findGitNewCommits($updates);
// TODO: Now, do content checks.
return 0; return 0;
} }
/**
* @task git
*/
private function parseGitUpdates($stdin) {
$updates = array();
$lines = phutil_split_lines($stdin, $retain_endings = false);
foreach ($lines as $line) {
$parts = explode(' ', $line, 3);
if (count($parts) != 3) {
throw new Exception(pht('Expected "old new ref", got "%s".', $line));
}
$update = array(
'old' => $parts[0],
'old.short' => substr($parts[0], 0, 8),
'new' => $parts[1],
'new.short' => substr($parts[1], 0, 8),
'ref' => $parts[2],
);
if (preg_match('(^refs/heads/)', $update['ref'])) {
$update['type'] = 'branch';
} else if (preg_match('(^refs/tags/)', $update['ref'])) {
$update['type'] = 'tag';
} else {
$update['type'] = 'unknown';
}
$updates[] = $update;
}
$updates = $this->findGitMergeBases($updates);
return $updates;
}
/**
* @task git
*/
private function findGitMergeBases(array $updates) {
$empty = str_repeat('0', 40);
$futures = array();
foreach ($updates as $key => $update) {
// Updates are in the form:
//
// <old hash> <new hash> <ref>
//
// If the old hash is "00000...", the ref is being created (either a new
// branch, or a new tag). If the new hash is "00000...", the ref is being
// deleted. If both are nonempty, the ref is being updated. For updates,
// we'll figure out the `merge-base` of the old and new objects here. This
// lets us reject non-FF changes cheaply; later, we'll figure out exactly
// which commits are new.
if ($update['old'] == $empty) {
$updates[$key]['operation'] = 'create';
} else if ($update['new'] == $empty) {
$updates[$key]['operation'] = 'delete';
} else {
$updates[$key]['operation'] = 'update';
$futures[$key] = $this->getRepository()->getLocalCommandFuture(
'merge-base %s %s',
$update['old'],
$update['new']);
}
}
foreach (Futures($futures)->limit(8) as $key => $future) {
list($stdout) = $future->resolvex();
$updates[$key]['merge-base'] = rtrim($stdout, "\n");
}
return $updates;
}
private function findGitNewCommits(array $updates) {
$futures = array();
foreach ($updates as $key => $update) {
if ($update['type'] == 'delete') {
// Deleting a branch or tag can never create any new commits.
continue;
}
// NOTE: This piece of magic finds all new commits, by walking backward
// from the new value to the value of *any* existing ref in the
// repository. Particularly, this will cover the cases of a new branch, a
// completely moved tag, etc.
$futures[$key] = $this->getRepository()->getLocalCommandFuture(
'log --format=%s %s --not --all',
'%H',
$update['new']);
}
foreach (Futures($futures)->limit(8) as $key => $future) {
list($stdout) = $future->resolvex();
$commits = phutil_split_lines($stdout, $retain_newlines = false);
$updates[$key]['commits'] = $commits;
}
return $updates;
}
private function executeSubversionHook() { private function executeSubversionHook() {
// TODO: Do useful things here, too. // TODO: Do useful things here, too.
@ -82,24 +196,4 @@ final class DiffusionCommitHookEngine extends Phobject {
return 0; return 0;
} }
private function parseGitUpdates($stdin) {
$updates = array();
$lines = phutil_split_lines($stdin, $retain_endings = false);
foreach ($lines as $line) {
$parts = explode(' ', $line, 3);
if (count($parts) != 3) {
throw new Exception(pht('Expected "old new ref", got "%s".', $line));
}
$updates[] = array(
'old' => $parts[0],
'new' => $parts[1],
'ref' => $parts[2],
);
}
return $updates;
}
} }