@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update templates used with mercurial to remove '--debug'

Summary:
Refer to discussion on D21677#275541

Refs D21681 (arcanist changes)

Phabricator has several uses of the `--debug` flag being used with Mercurial. Use of this flag causes additional output to be added which Phabricator needs, however the behavior of `--debug` is not guaranteed to be stable, and in newer versions of Mercurial there have been additional output that has caused Phabricator to choke on parsing the output. This change removes several uses of `--debug` in favor of using `--template` with the `hg log` or `hg annotate` commands in combination with the `{p1.node}` or `{p2.node}` template format.

The use of `{p1node}` format in templates was added in [[ https://www.mercurial-scm.org/wiki/WhatsNew/Archive#Mercurial_2.4_.282012-11-01.29 | Mercurial 2.4 (2012) ]]. This format was deprecated in [[ https://www.mercurial-scm.org/wiki/WhatsNew#Mercurial_4.9_.282019-02-01.29 | Mercurial 4.9 (2019) ]] in favor of using `{p1.node}` format which is unclear when this new format was added (presumably earlier than Mercurial 4.9).

The use of `--template` with `hg annotate` is only officially supported in [[ https://www.mercurial-scm.org/wiki/Release4.6 | Mercurial 4.6 (2018) ]], though does appear to work in 4.5 but is not documented.

Since the `{p1node}` format was introduced in 2.4 this bumps the required version of `hg` to 2.4 (from 1.9). Since the `annotate --template` feature wasn't added until 4.6 (which is still fairly recent), the use of it is gated on a capability test, but still preferred for use where possible to avoid extraneous output from `--debug` flag.

Test Plan:
I verified I could do the following in a mercurial repository, while having mercurial 5.8 installed:
1. Navigate and view files in Diffusion under e.g. `/source/test-repo/`.
2. While viewing a file in Diffusion verified that I could view the blame of the file and the history/annotations looked accurate for the files I was browsing.
3. From the blame sidebar, select to view a commit which loaded and displayed changes properly.
4. View the history of the repository under e.g. `/source/test-repo/history/default/`. I verified the history looked correct and the tree-like structure showing relationship of commits also looked accurate.

I setup mercurial to run version 4.4, created a new repository, added some commits, and verified all the above behavior still works properly.

Reviewers: #blessed_reviewers, epriestley

Reviewed By: #blessed_reviewers, epriestley

Subscribers: Korvin, epriestley

Differential Revision: https://secure.phabricator.com/D21679

+120 -111
+5 -11
src/applications/config/check/PhabricatorBinariesSetupCheck.php
··· 120 120 break; 121 121 case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: 122 122 $bad_versions = array( 123 - // We need 1.9 for HTTP cloning, see T3046. 124 - '< 1.9' => pht( 125 - 'The minimum supported version of Mercurial is 1.9, which was '. 126 - 'released in 2011.'), 127 - '= 2.1' => pht( 128 - 'This version of Mercurial returns a bad exit code '. 129 - 'after a successful pull.'), 130 - '= 2.2' => pht( 131 - 'This version of Mercurial has a significant memory leak, fixed '. 132 - 'in 2.2.1. Pushing fails with this version as well; see %s.', 133 - 'T3046#54922'), 123 + // We need 2.4 for utilizing `{p1node}` keyword in templates, see 124 + // D21679 and D21681. 125 + '< 2.4' => pht( 126 + 'The minimum supported version of Mercurial is 2.4, which was '. 127 + 'released in 2012.'), 134 128 ); 135 129 break; 136 130 }
+23 -36
src/applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php
··· 136 136 // stop history (this is more consistent with the Mercurial worldview of 137 137 // branches). 138 138 139 + $path_args = array(); 139 140 if (strlen($path)) { 140 - $path_arg = csprintf('%s', $path); 141 + $path_args[] = $path; 141 142 $revset_arg = hgsprintf( 142 143 'reverse(ancestors(%s))', 143 144 $commit_hash); 144 145 } else { 145 - $path_arg = ''; 146 146 $revset_arg = hgsprintf( 147 147 'reverse(ancestors(%s)) and branch(%s)', 148 148 $drequest->getBranch(), 149 149 $commit_hash); 150 150 } 151 151 152 + $hg_analyzer = PhutilBinaryAnalyzer::getForBinary('hg'); 153 + if ($hg_analyzer->isMercurialTemplatePnodeAvailable()) { 154 + $hg_log_template = '{node} {p1.node} {p2.node}\\n'; 155 + } else { 156 + $hg_log_template = '{node} {p1node} {p2node}\\n'; 157 + } 158 + 152 159 list($stdout) = $repository->execxLocalCommand( 153 - 'log --debug --template %s --limit %d --rev %s -- %C', 154 - '{node};{parents}\\n', 160 + 'log --template %s --limit %d --rev %s -- %Ls', 161 + $hg_log_template, 155 162 ($offset + $limit), // No '--skip' in Mercurial. 156 163 $revset_arg, 157 - $path_arg); 164 + $path_args); 158 165 159 - $stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( 160 - $stdout); 161 166 $lines = explode("\n", trim($stdout)); 162 167 $lines = array_slice($lines, $offset); 163 168 ··· 166 171 167 172 $last = null; 168 173 foreach (array_reverse($lines) as $line) { 169 - // In the event additional log output is included in future mercurial 170 - // updates, if the line does not contain any semi-colon then log it and 171 - // ignore it. 172 - if (strpos($line, ';') === false) { 173 - phlog(pht( 174 - 'Unexpected output from mercurial "log --debug" command: %s', 175 - $line)); 176 - continue; 174 + $parts = explode(' ', trim($line)); 175 + $hash = $parts[0]; 176 + $parents = array_slice($parts, 1, 2); 177 + foreach ($parents as $parent) { 178 + if (!preg_match('/^0+\z/', $parent)) { 179 + $parent_map[$hash][] = $parent; 180 + } 177 181 } 178 - list($hash, $parents) = explode(';', $line); 179 - $parents = trim($parents); 180 - if (!$parents) { 181 - if ($last === null) { 182 - $parent_map[$hash] = array('...'); 183 - } else { 184 - $parent_map[$hash] = array($last); 185 - } 186 - } else { 187 - $parents = preg_split('/\s+/', $parents); 188 - foreach ($parents as $parent) { 189 - list($plocal, $phash) = explode(':', $parent); 190 - if (!preg_match('/^0+$/', $phash)) { 191 - $parent_map[$hash][] = $phash; 192 - } 193 - } 194 - // This may happen for the zeroth commit in repository, both hashes 195 - // are "000000000...". 196 - if (empty($parent_map[$hash])) { 197 - $parent_map[$hash] = array('...'); 198 - } 182 + // This may happen for the zeroth commit in repository, both hashes 183 + // are "000000000...". 184 + if (empty($parent_map[$hash])) { 185 + $parent_map[$hash] = array('...'); 199 186 } 200 187 201 188 // The rendering code expects the first commit to be "mainline", like
+47
src/applications/diffusion/protocol/__tests__/DiffusionMercurialCommandEngineTests.php
··· 3 3 final class DiffusionMercurialCommandEngineTests extends PhabricatorTestCase { 4 4 5 5 public function testFilteringDebugOutput() { 6 + $map = array( 7 + '' => '', 8 + 9 + "quack\n" => "quack\n", 10 + 11 + "ignoring untrusted configuration option x.y = z\nquack\n" => 12 + "quack\n", 13 + 14 + "ignoring untrusted configuration option x.y = z\n". 15 + "ignoring untrusted configuration option x.y = z\n". 16 + "quack\n" => 17 + "quack\n", 18 + 19 + "ignoring untrusted configuration option x.y = z\n". 20 + "ignoring untrusted configuration option x.y = z\n". 21 + "ignoring untrusted configuration option x.y = z\n". 22 + "quack\n" => 23 + "quack\n", 24 + 25 + "quack\n". 26 + "ignoring untrusted configuration option x.y = z\n". 27 + "ignoring untrusted configuration option x.y = z\n". 28 + "ignoring untrusted configuration option x.y = z\n" => 29 + "quack\n", 30 + 31 + "ignoring untrusted configuration option x.y = z\n". 32 + "ignoring untrusted configuration option x.y = z\n". 33 + "duck\n". 34 + "ignoring untrusted configuration option x.y = z\n". 35 + "ignoring untrusted configuration option x.y = z\n". 36 + "bread\n". 37 + "ignoring untrusted configuration option x.y = z\n". 38 + "quack\n" => 39 + "duck\nbread\nquack\n", 40 + 41 + "ignoring untrusted configuration option x.y = z\n". 42 + "duckignoring untrusted configuration option x.y = z\n". 43 + "quack" => 44 + 'duckquack', 45 + ); 46 + 47 + foreach ($map as $input => $expect) { 48 + $actual = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( 49 + $input); 50 + $this->assertEqual($expect, $actual, $input); 51 + } 52 + 6 53 // Output that should be filtered out from the results 7 54 $output = 8 55 "ignoring untrusted configuration option\n".
+33 -3
src/applications/diffusion/query/blame/DiffusionMercurialBlameQuery.php
··· 6 6 $repository = $request->getRepository(); 7 7 $commit = $request->getCommit(); 8 8 9 - // NOTE: We're using "--debug" to make "--changeset" give us the full 10 - // commit hashes. 9 + // NOTE: Using "--template" or "--debug" to get the full commit hashes. 10 + $hg_analyzer = PhutilBinaryAnalyzer::getForBinary('hg'); 11 + if ($hg_analyzer->isMercurialAnnotateTemplatesAvailable()) { 12 + // See `hg help annotate --verbose` for more info on the template format. 13 + // Use array of arguments so the template line does not need wrapped in 14 + // quotes. 15 + $template = array( 16 + '--template', 17 + "{lines % '{node}: {line}'}", 18 + ); 19 + } else { 20 + $template = array( 21 + '--debug', 22 + '--changeset', 23 + ); 24 + } 11 25 12 26 return $repository->getLocalCommandFuture( 13 - 'annotate --debug --changeset --rev %s -- %s', 27 + 'annotate %Ls --rev %s -- %s', 28 + $template, 14 29 $commit, 15 30 $path); 16 31 } ··· 26 41 27 42 $lines = phutil_split_lines($stdout); 28 43 foreach ($lines as $line) { 44 + // If the `--debug` flag was used above instead of `--template` then 45 + // there's a good change additional output was included which is not 46 + // relevant to the information we want. It should be safe to call this 47 + // regardless of whether we used `--debug` or `--template` so there isn't 48 + // a need to track which argument was used. 49 + $line = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( 50 + $line); 51 + 52 + // Just in case new versions of Mercurial add arbitrary output when using 53 + // the `--debug`, do a quick sanity check that this line is formatted in 54 + // a way we're expecting. 55 + if (strpos($line, ':') === false) { 56 + phlog(pht('Unexpected output from hg annotate: %s', $line)); 57 + continue; 58 + } 29 59 list($commit) = explode(':', $line, 2); 30 60 $result[] = $commit; 31 61 }
+12 -12
src/applications/diffusion/query/lowlevel/DiffusionLowLevelParentsQuery.php
··· 47 47 private function loadMercurialParents() { 48 48 $repository = $this->getRepository(); 49 49 50 + $hg_analyzer = PhutilBinaryAnalyzer::getForBinary('hg'); 51 + if ($hg_analyzer->isMercurialTemplatePnodeAvailable()) { 52 + $hg_log_template = '{p1.node} {p2.node}'; 53 + } else { 54 + $hg_log_template = '{p1node} {p2node}'; 55 + } 56 + 50 57 list($stdout) = $repository->execxLocalCommand( 51 - 'log --debug --limit 1 --template={parents} --rev %s', 58 + 'log --limit 1 --template %s --rev %s', 59 + $hg_log_template, 52 60 $this->identifier); 53 61 54 - $stdout = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( 55 - $stdout); 56 - 57 62 $hashes = preg_split('/\s+/', trim($stdout)); 58 63 foreach ($hashes as $key => $value) { 59 - // Mercurial parents look like "23:ad9f769d6f786fad9f76d9a" -- we want 60 - // to strip out the local rev part. 61 - list($local, $global) = explode(':', $value); 62 - $hashes[$key] = $global; 63 - 64 - // With --debug we get 40-character hashes but also get the "000000..." 65 - // hash for missing parents; ignore it. 66 - if (preg_match('/^0+$/', $global)) { 64 + // We get 40-character hashes but also get the "000000..." hash for 65 + // missing parents; ignore it. 66 + if (preg_match('/^0+\z/', $value)) { 67 67 unset($hashes[$key]); 68 68 } 69 69 }
-49
src/applications/repository/storage/__tests__/PhabricatorRepositoryTestCase.php
··· 100 100 101 101 } 102 102 103 - public function testFilterMercurialDebugOutput() { 104 - $map = array( 105 - '' => '', 106 - 107 - "quack\n" => "quack\n", 108 - 109 - "ignoring untrusted configuration option x.y = z\nquack\n" => 110 - "quack\n", 111 - 112 - "ignoring untrusted configuration option x.y = z\n". 113 - "ignoring untrusted configuration option x.y = z\n". 114 - "quack\n" => 115 - "quack\n", 116 - 117 - "ignoring untrusted configuration option x.y = z\n". 118 - "ignoring untrusted configuration option x.y = z\n". 119 - "ignoring untrusted configuration option x.y = z\n". 120 - "quack\n" => 121 - "quack\n", 122 - 123 - "quack\n". 124 - "ignoring untrusted configuration option x.y = z\n". 125 - "ignoring untrusted configuration option x.y = z\n". 126 - "ignoring untrusted configuration option x.y = z\n" => 127 - "quack\n", 128 - 129 - "ignoring untrusted configuration option x.y = z\n". 130 - "ignoring untrusted configuration option x.y = z\n". 131 - "duck\n". 132 - "ignoring untrusted configuration option x.y = z\n". 133 - "ignoring untrusted configuration option x.y = z\n". 134 - "bread\n". 135 - "ignoring untrusted configuration option x.y = z\n". 136 - "quack\n" => 137 - "duck\nbread\nquack\n", 138 - 139 - "ignoring untrusted configuration option x.y = z\n". 140 - "duckignoring untrusted configuration option x.y = z\n". 141 - "quack" => 142 - 'duckquack', 143 - ); 144 - 145 - foreach ($map as $input => $expect) { 146 - $actual = DiffusionMercurialCommandEngine::filterMercurialDebugOutput( 147 - $input); 148 - $this->assertEqual($expect, $actual, $input); 149 - } 150 - } 151 - 152 103 public function testRepositoryShortNameValidation() { 153 104 $good = array( 154 105 'sensible-repository',