Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14011955
D7763.id17560.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
10 KB
Referenced Files
None
Subscribers
None
D7763.id17560.diff
View Options
Index: src/applications/diffusion/conduit/ConduitAPI_diffusion_commitbranchesquery_Method.php
===================================================================
--- src/applications/diffusion/conduit/ConduitAPI_diffusion_commitbranchesquery_Method.php
+++ src/applications/diffusion/conduit/ConduitAPI_diffusion_commitbranchesquery_Method.php
@@ -48,6 +48,8 @@
$repository = $drequest->getRepository();
$commit = $request->getValue('commit');
+ // TODO: This should use `branches`.
+
list($contains) = $repository->execxLocalCommand(
'log --template %s --limit 1 --rev %s --',
'{branch}',
Index: src/applications/diffusion/engine/DiffusionCommitHookEngine.php
===================================================================
--- src/applications/diffusion/engine/DiffusionCommitHookEngine.php
+++ src/applications/diffusion/engine/DiffusionCommitHookEngine.php
@@ -345,10 +345,9 @@
$ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DANGEROUS;
$dangerous = pht(
- "DANGEROUS CHANGE: The change you're attempting to push updates ".
- "the branch '%s' from '%s' to '%s', but this is not a ".
- "fast-forward. Pushes which rewrite published branch history ".
- "are dangerous.",
+ "The change you're attempting to push updates the branch '%s' ".
+ "from '%s' to '%s', but this is not a fast-forward. Pushes ".
+ "which rewrite published branch history are dangerous.",
$ref_update->getRefName(),
$ref_update->getRefOldShort(),
$ref_update->getRefNewShort());
@@ -414,8 +413,215 @@
private function findMercurialRefUpdates() {
- // TODO: Implement.
- return array();
+ $hg_node = getenv('HG_NODE');
+ if (!$hg_node) {
+ throw new Exception(pht('Expected HG_NODE in environment!'));
+ }
+
+ // NOTE: We need to make sure this is passed to subprocesses, or they won't
+ // be able to see new commits. Mercurial uses this as a marker to determine
+ // whether the pending changes are visible or not.
+ $_ENV['HG_PENDING'] = getenv('HG_PENDING');
+ $repository = $this->getRepository();
+
+ $futures = array();
+
+ foreach (array('old', 'new') as $key) {
+ $futures[$key] = $repository->getLocalCommandFuture(
+ 'heads --template %s',
+ '{node}\1{branches}\2');
+ }
+ // Wipe HG_PENDING out of the old environment so we see the pre-commit
+ // state of the repository.
+ $futures['old']->updateEnv('HG_PENDING', null);
+
+ $futures['commits'] = $repository->getLocalCommandFuture(
+ "log --rev %s --rev tip --template %s",
+ hgsprintf('%s', $hg_node),
+ '{node}\1{branches}\2');
+
+ foreach ($futures as $future) {
+ $future->resolvex();
+ }
+
+ list($commit_raw) = $futures['commits']->resolvex();
+ $commit_map = $this->parseMercurialCommits($commit_raw);
+
+ list($old_raw) = $futures['old']->resolvex();
+ $old_refs = $this->parseMercurialHeads($old_raw);
+
+ list($new_raw) = $futures['new']->resolvex();
+ $new_refs = $this->parseMercurialHeads($new_raw);
+
+ $all_refs = array_keys($old_refs + $new_refs);
+
+ $ref_updates = array();
+ foreach ($all_refs as $ref) {
+ $old_heads = idx($old_refs, $ref, array());
+ $new_heads = idx($new_refs, $ref, array());
+
+ sort($old_heads);
+ sort($new_heads);
+
+ if ($old_heads === $new_heads) {
+ // No changes to this branch, so skip it.
+ continue;
+ }
+
+ if (!$new_heads) {
+ if ($old_heads) {
+ // It looks like this push deletes a branch, but that isn't possible
+ // in Mercurial, so something is going wrong here. Bail out.
+ throw new Exception(
+ pht(
+ 'Mercurial repository has no new head for branch "%s" after '.
+ 'push. This is unexpected; rejecting change.'));
+ } else {
+ // Obviously, this should never be possible either, as it makes
+ // no sense. Explode.
+ throw new Exception(
+ pht(
+ 'Mercurial repository has no new or old heads for branch "%s" '.
+ 'after push. This makes no sense; rejecting change.'));
+ }
+ }
+
+ $stray_heads = array();
+ if (count($old_heads) > 1) {
+ // HORRIBLE: In Mercurial, branches can have multiple heads. If the
+ // old branch had multiple heads, we need to figure out which new
+ // heads descend from which old heads, so we can tell whether you're
+ // actively creating new heads (dangerous) or just working in a
+ // repository that's already full of garbage (strongly discouraged but
+ // not as inherently dangerous). These cases should be very uncommon.
+
+ $dfutures = array();
+ foreach ($old_heads as $old_head) {
+ $dfutures[$old_head] = $repository->getLocalCommandFuture(
+ 'log --rev %s --template %s',
+ hgsprintf('(descendants(%s) and head())', $old_head),
+ '{node}\1');
+ }
+
+ $head_map = array();
+ foreach (Futures($dfutures) as $future_head => $dfuture) {
+ list($stdout) = $dfuture->resolvex();
+ $head_map[$future_head] = array_filter(explode("\1", $stdout));
+ }
+
+ // Now, find all the new stray heads this push creates, if any. These
+ // are new heads which do not descend from the old heads.
+ $seen = array_fuse(array_mergev($head_map));
+ foreach ($new_heads as $new_head) {
+ if (empty($seen[$new_head])) {
+ $head_map[self::EMPTY_HASH][] = $new_head;
+ }
+ }
+ } else if ($old_heads) {
+ $head_map[head($old_heads)] = $new_heads;
+ } else {
+ $head_map[self::EMPTY_HASH] = $new_heads;
+ }
+
+ foreach ($head_map as $old_head => $child_heads) {
+ foreach ($child_heads as $new_head) {
+ if ($new_head === $old_head) {
+ continue;
+ }
+
+ $ref_flags = 0;
+ $dangerous = null;
+ if ($old_head == self::EMPTY_HASH) {
+ $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_ADD;
+ } else {
+ $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_APPEND;
+ }
+
+ $splits_existing_head = (count($child_heads) > 1);
+ $creates_duplicate_head = ($old_head == self::EMPTY_HASH) &&
+ (count($head_map) > 1);
+
+ if ($splits_existing_head || $creates_duplicate_head) {
+ $readable_child_heads = array();
+ foreach ($child_heads as $child_head) {
+ $readable_child_heads[] = substr($child_head, 0, 12);
+ }
+
+ $ref_flags |= PhabricatorRepositoryPushLog::CHANGEFLAG_DANGEROUS;
+
+ if ($splits_existing_head) {
+ // We're splitting an existing head into two or more heads.
+ // This is dangerous, and a super bad idea. Note that we're only
+ // raising this if you're actively splitting a branch head. If a
+ // head split in the past, we don't consider appends to it
+ // to be dangerous.
+ $dangerous = pht(
+ "The change you're attempting to push splits the head of ".
+ "branch '%s' into multiple heads: %s. This is inadvisable ".
+ "and dangerous.",
+ $ref,
+ implode(', ', $readable_child_heads));
+ } else {
+ // We're adding a second (or more) head to a branch. The new
+ // head is not a descendant of any old head.
+ $dangerous = pht(
+ "The change you're attempting to push creates new, divergent ".
+ "heads for the branch '%s': %s. This is inadvisable and ".
+ "dangerous.",
+ $ref,
+ implode(', ', $readable_child_heads));
+ }
+ }
+
+ $ref_update = $this->newPushLog()
+ ->setRefType(PhabricatorRepositoryPushLog::REFTYPE_BRANCH)
+ ->setRefName($ref)
+ ->setRefOld($old_head)
+ ->setRefNew($new_head)
+ ->setChangeFlags($ref_flags);
+
+ if ($dangerous !== null) {
+ $ref_update->attachDangerousChangeDescription($dangerous);
+ }
+
+ $ref_updates[] = $ref_update;
+ }
+ }
+ }
+
+ return $ref_updates;
+ }
+
+ private function parseMercurialCommits($raw) {
+ $commits_lines = explode("\2", $raw);
+ $commits_lines = array_filter($commits_lines);
+ $commit_map = array();
+ foreach ($commits_lines as $commit_line) {
+ list($node, $branches_raw) = explode("\1", $commit_line);
+
+ if (!strlen($branches_raw)) {
+ $branches = array('default');
+ } else {
+ $branches = explode(' ', $branches_raw);
+ }
+
+ $commit_map[$node] = $branches;
+ }
+
+ return $commit_map;
+ }
+
+ private function parseMercurialHeads($raw) {
+ $heads_map = $this->parseMercurialCommits($raw);
+
+ $heads = array();
+ foreach ($heads_map as $commit => $branches) {
+ foreach ($branches as $branch) {
+ $heads[$branch][] = $commit;
+ }
+ }
+
+ return $heads;
}
private function findMercurialContentUpdates(array $ref_updates) {
Index: src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
===================================================================
--- src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
+++ src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
@@ -735,8 +735,8 @@
'URI "%s".',
$expect_remote));
$repository->execxLocalCommand(
- 'remote add origin %s',
- $expect_remote);
+ 'remote add origin %P',
+ $repository->getRemoteURIEnvelope());
// NOTE: This doesn't fetch the origin (it just creates it), so we won't
// know about origin branches until the next "pull" happens. That's fine
@@ -751,8 +751,8 @@
$remote_uri,
$expect_remote));
$repository->execxLocalCommand(
- 'remote set-url origin %s',
- $expect_remote);
+ 'remote set-url origin %P',
+ $repository->getRemoteURIEnvelope());
} else {
// Bad remote and we aren't comfortable repairing it.
$message = pht(
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 2, 6:17 AM (2 w, 4 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6740558
Default Alt Text
D7763.id17560.diff (10 KB)
Attached To
Mode
D7763: Generate ref updates in Mercurial hooks
Attached
Detach File
Event Timeline
Log In to Comment