Page MenuHomePhabricator

D7763.id17560.diff
No OneTemporary

D7763.id17560.diff

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

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)

Event Timeline