libfetchers/mercurial: handle "evil refs" gracefully

If a revision is specified in a way that looks like a commit hash, Lix expects
that it actually is a commit hash. Unlike Git, Mercurial will fall back to
bookmarks, tags and branches with the specified name when a commit with the
specified hash does not exist. Previously, an assertion failure would be thrown
due to the resulting commit hash mismatch. Tell Mercurial to only take commit
hashes into account, whose non-existence is then handled gracefully.

Change-Id: I98bf020187575f3cf8176831da85872d066c4d95
This commit is contained in:
Alois Wohlschlager
2025-01-26 11:22:03 +01:00
parent 4af6b5ed9f
commit a2daf4e774
2 changed files with 9 additions and 4 deletions
+4 -4
View File
@@ -246,7 +246,7 @@ struct MercurialInputScheme : InputScheme
return makeResult(res->first, std::move(res->second));
}
auto revOrRef = input.getRev() ? input.getRev()->gitRev() : *input.getRef();
auto revOrRef = input.getRev() ? fmt("id(%s)", input.getRev()->gitRev()) : *input.getRef();
Attrs unlockedAttrs({
{"type", "hg"},
@@ -269,7 +269,7 @@ struct MercurialInputScheme : InputScheme
have to pull again. */
if (!(input.getRev()
&& pathExists(cacheDir)
&& runProgram(hgOptions({ "log", "-R", cacheDir, "-r", input.getRev()->gitRev(), "--template", "1" })).second == "1"))
&& runProgram(hgOptions({ "identify", "-R", cacheDir, "-r", revOrRef, "--template", "1" })).second == "1"))
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", actualUrl));
@@ -294,7 +294,7 @@ struct MercurialInputScheme : InputScheme
}
auto tokens = tokenizeString<std::vector<std::string>>(
runHg({ "log", "-R", cacheDir, "-r", revOrRef, "--template", "{node} {count(revset('::{rev}'))} {branch}" }));
runHg({ "identify", "-R", cacheDir, "-r", revOrRef, "--template", "{node} {count(revset('::{rev}'))} {branch}" }));
assert(tokens.size() == 3);
input.attrs.insert_or_assign("rev", Hash::parseAny(tokens[0], HashType::SHA1).gitRev());
@@ -307,7 +307,7 @@ struct MercurialInputScheme : InputScheme
Path tmpDir = createTempDir();
AutoDelete delTmpDir(tmpDir, true);
runHg({ "archive", "-R", cacheDir, "-r", input.getRev()->gitRev(), tmpDir });
runHg({ "archive", "-R", cacheDir, "-r", fmt("id(%s)", input.getRev()->gitRev()), tmpDir });
deletePath(tmpDir + "/.hg_archival.txt");
+5
View File
@@ -26,6 +26,7 @@ rev1=$(hg log --cwd $repo -r tip --template '{node}')
echo world > $repo/hello
hg commit --cwd $repo -m 'Bla2'
rev2=$(hg log --cwd $repo -r tip --template '{node}')
hg --cwd $repo bookmark aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
# Fetch an unclean branch.
echo unclean > $repo/hello
@@ -45,6 +46,10 @@ path=$(nix eval --impure --raw --expr "(builtins.fetchMercurial \"file://$repo\"
path2=$(nix eval --impure --raw --expr "(builtins.fetchMercurial { url = \"file://$repo\"; rev = \"$rev2\"; }).outPath")
[[ $path = $path2 ]]
out=$(nix eval --impure --raw --expr "builtins.fetchMercurial { url = \"file://$repo\"; rev = \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"; }" 2>&1) || status=$?
[[ $status == 1 ]]
[[ $out =~ 'hg failed with exit code' ]]
# In pure eval mode, fetchMercurial with a revision should succeed.
[[ $(nix eval --raw --expr "builtins.readFile (fetchMercurial { url = \"file://$repo\"; rev = \"$rev2\"; } + \"/hello\")") = world ]]