From a2daf4e7743cc7ab2cdc4ec5a5adc129cbad82d7 Mon Sep 17 00:00:00 2001 From: Alois Wohlschlager Date: Sun, 26 Jan 2025 11:22:03 +0100 Subject: [PATCH] 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 --- lix/libfetchers/mercurial.cc | 8 ++++---- tests/functional/fetchMercurial.sh | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lix/libfetchers/mercurial.cc b/lix/libfetchers/mercurial.cc index f99191566..bd3037e3c 100644 --- a/lix/libfetchers/mercurial.cc +++ b/lix/libfetchers/mercurial.cc @@ -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>( - 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"); diff --git a/tests/functional/fetchMercurial.sh b/tests/functional/fetchMercurial.sh index 001e3ae00..42d838766 100644 --- a/tests/functional/fetchMercurial.sh +++ b/tests/functional/fetchMercurial.sh @@ -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 ]]