PageRenderTime 59ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/applications/repository/daemon/commitdiscovery/mercurial/PhabricatorRepositoryMercurialCommitDiscoveryDaemon.php

http://github.com/facebook/phabricator
PHP | 137 lines | 78 code | 20 blank | 39 comment | 11 complexity | df29632ea32ad8854834ae4814eb09d7 MD5 | raw file
Possible License(s): JSON, MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause, LGPL-2.0, MIT, LGPL-2.1, LGPL-3.0
  1. <?php
  2. /*
  3. * Copyright 2011 Facebook, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. class PhabricatorRepositoryMercurialCommitDiscoveryDaemon
  18. extends PhabricatorRepositoryCommitDiscoveryDaemon {
  19. protected function discoverCommits() {
  20. $repository = $this->getRepository();
  21. $vcs = $repository->getVersionControlSystem();
  22. if ($vcs != PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL) {
  23. throw new Exception("Repository is not a Mercurial repository.");
  24. }
  25. $repository_phid = $repository->getPHID();
  26. list($stdout) = $repository->execxLocalCommand('branches');
  27. $branches = ArcanistMercurialParser::parseMercurialBranches($stdout);
  28. $got_something = false;
  29. foreach ($branches as $name => $branch) {
  30. $commit = $branch['rev'];
  31. $commit = $this->getFullHash($commit);
  32. if ($this->isKnownCommit($commit)) {
  33. continue;
  34. } else {
  35. $this->discoverCommit($commit);
  36. $got_something = true;
  37. }
  38. }
  39. return $got_something;
  40. }
  41. private function getFullHash($commit) {
  42. // NOTE: Mercurial shortens hashes to 12 characters by default. This
  43. // implies collisions with as few as a few million commits. The
  44. // documentation sensibly advises "Do not use short-form IDs for
  45. // long-lived representations". It then continues "You can use the
  46. // --debug option to display the full changeset ID". What?! Yes, this
  47. // is in fact the only way to turn on full hashes, and the hg source
  48. // code is littered with "hexfn = ui.debugflag and hex or short" and
  49. // similar. There is no more-selective flag or config option.
  50. //
  51. // Unfortunately, "hg --debug" turns on tons of other extra output,
  52. // including full commit messages in "hg log" and "hg parents" (which
  53. // ignore --style); this renders them unparseable. So we have to use
  54. // "hg id" to convert short hashes into full hashes. See:
  55. //
  56. // <http://mercurial.selenic.com/wiki/ChangeSetID>
  57. //
  58. // Of course, this means that if there are collisions we will break here
  59. // (the short commit identifier won't be unambiguous) but maybe Mercurial
  60. // will have a --full-hashes flag or something by then and we can fix it
  61. // properly. Until we run into that, this allows us to store data in the
  62. // right format so when we eventually encounter this we won't have to
  63. // reparse every Mercurial repository.
  64. $repository = $this->getRepository();
  65. list($stdout) = $repository->execxLocalCommand(
  66. 'id --debug -i --rev %s',
  67. $commit);
  68. return trim($stdout);
  69. }
  70. private function discoverCommit($commit) {
  71. $discover = array();
  72. $insert = array();
  73. $repository = $this->getRepository();
  74. $discover[] = $commit;
  75. $insert[] = $commit;
  76. $seen_parent = array();
  77. // For all the new commits at the branch heads, walk backward until we find
  78. // only commits we've aleady seen.
  79. while (true) {
  80. $target = array_pop($discover);
  81. list($stdout) = $repository->execxLocalCommand(
  82. 'parents --style default --rev %s',
  83. $target);
  84. $parents = ArcanistMercurialParser::parseMercurialLog($stdout);
  85. if ($parents) {
  86. foreach ($parents as $parent) {
  87. $parent_commit = $parent['rev'];
  88. $parent_commit = $this->getFullHash($parent_commit);
  89. if (isset($seen_parent[$parent_commit])) {
  90. continue;
  91. }
  92. $seen_parent[$parent_commit] = true;
  93. if (!$this->isKnownCommit($parent_commit)) {
  94. $discover[] = $parent_commit;
  95. $insert[] = $parent_commit;
  96. }
  97. }
  98. }
  99. if (empty($discover)) {
  100. break;
  101. }
  102. $this->stillWorking();
  103. }
  104. while (true) {
  105. $target = array_pop($insert);
  106. list($stdout) = $repository->execxLocalCommand(
  107. 'log --rev %s --template %s',
  108. $target,
  109. '{date|rfc822date}');
  110. $epoch = strtotime($stdout);
  111. $this->recordCommit($target, $epoch);
  112. if (empty($insert)) {
  113. break;
  114. }
  115. }
  116. }
  117. }