diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 09508c602..e93d58709 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -322,8 +322,17 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this for (size_t n = 0; n < git_commit_parentcount(commit->get()); ++n) { git_commit * parent; - if (git_commit_parent(&parent, commit->get(), n)) - throw Error("getting parent of Git commit '%s': %s", *git_commit_id(commit->get()), git_error_last()->message); + if (git_commit_parent(&parent, commit->get(), n)) { + throw Error( + "Failed to retrieve the parent of Git commit '%s': %s. " + "This may be due to an incomplete repository history. " + "To resolve this, either enable the shallow parameter in your flake URL (?shallow=1) " + "or add set the shallow parameter to true in builtins.fetchGit, " + "or fetch the complete history for this branch.", + *git_commit_id(commit->get()), + git_error_last()->message + ); + } todo.push(Commit(parent)); } } diff --git a/tests/functional/fetchGit.sh b/tests/functional/fetchGit.sh index 5e5e8e61f..fcae8bf69 100755 --- a/tests/functional/fetchGit.sh +++ b/tests/functional/fetchGit.sh @@ -12,7 +12,7 @@ repo=$TEST_ROOT/./git export _NIX_FORCE_HTTP=1 -rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix $TEST_ROOT/worktree $TEST_ROOT/shallow $TEST_ROOT/minimal +rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix $TEST_ROOT/worktree $TEST_ROOT/minimal git init $repo git -C $repo config user.email "foobar@example.com" @@ -216,18 +216,6 @@ git -C $TEST_ROOT/minimal fetch $repo $rev2 git -C $TEST_ROOT/minimal checkout $rev2 [[ $(nix eval --impure --raw --expr "(builtins.fetchGit { url = $TEST_ROOT/minimal; }).rev") = $rev2 ]] -# Fetching a shallow repo shouldn't work by default, because we can't -# return a revCount. -git clone --depth 1 file://$repo $TEST_ROOT/shallow -(! nix eval --impure --raw --expr "(builtins.fetchGit { url = $TEST_ROOT/shallow; ref = \"dev\"; }).outPath") - -# But you can request a shallow clone, which won't return a revCount. -path6=$(nix eval --impure --raw --expr "(builtins.fetchTree { type = \"git\"; url = \"file://$TEST_ROOT/shallow\"; ref = \"dev\"; shallow = true; }).outPath") -[[ $path3 = $path6 ]] -[[ $(nix eval --impure --expr "(builtins.fetchTree { type = \"git\"; url = \"file://$TEST_ROOT/shallow\"; ref = \"dev\"; shallow = true; }).revCount or 123") == 123 ]] - -expectStderr 1 nix eval --expr 'builtins.fetchTree { type = "git"; url = "file:///foo"; }' | grepQuiet "'fetchTree' will not fetch unlocked input" - # Explicit ref = "HEAD" should work, and produce the same outPath as without ref path7=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"HEAD\"; }).outPath") path8=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; }).outPath") diff --git a/tests/functional/fetchGitShallow.sh b/tests/functional/fetchGitShallow.sh new file mode 100644 index 000000000..cf7e5fd4f --- /dev/null +++ b/tests/functional/fetchGitShallow.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +# shellcheck source=common.sh +source common.sh + +requireGit + +# Create a test repo with multiple commits for all our tests +git init "$TEST_ROOT/shallow-parent" +git -C "$TEST_ROOT/shallow-parent" config user.email "foobar@example.com" +git -C "$TEST_ROOT/shallow-parent" config user.name "Foobar" + +# Add several commits to have history +echo "{ outputs = _: {}; }" > "$TEST_ROOT/shallow-parent/flake.nix" +echo "" > "$TEST_ROOT/shallow-parent/file.txt" +git -C "$TEST_ROOT/shallow-parent" add file.txt flake.nix +git -C "$TEST_ROOT/shallow-parent" commit -m "First commit" + +echo "second" > "$TEST_ROOT/shallow-parent/file.txt" +git -C "$TEST_ROOT/shallow-parent" commit -m "Second commit" -a + +echo "third" > "$TEST_ROOT/shallow-parent/file.txt" +git -C "$TEST_ROOT/shallow-parent" commit -m "Third commit" -a + +# Add a branch for testing ref fetching +git -C "$TEST_ROOT/shallow-parent" checkout -b dev +echo "branch content" > "$TEST_ROOT/shallow-parent/branch-file.txt" +git -C "$TEST_ROOT/shallow-parent" add branch-file.txt +git -C "$TEST_ROOT/shallow-parent" commit -m "Branch commit" + +# Make a shallow clone (depth=1) +git clone --depth 1 "file://$TEST_ROOT/shallow-parent" "$TEST_ROOT/shallow-clone" + +# Test 1: Fetching a shallow repo shouldn't work by default, because we can't +# return a revCount. +(! nix eval --impure --raw --expr "(builtins.fetchGit { url = \"$TEST_ROOT/shallow-clone\"; ref = \"dev\"; }).outPath") + +# Test 2: But you can request a shallow clone, which won't return a revCount. +path=$(nix eval --impure --raw --expr "(builtins.fetchTree { type = \"git\"; url = \"file://$TEST_ROOT/shallow-clone\"; ref = \"dev\"; shallow = true; }).outPath") +# Verify file from dev branch exists +[[ -f "$path/branch-file.txt" ]] +# Verify revCount is missing +[[ $(nix eval --impure --expr "(builtins.fetchTree { type = \"git\"; url = \"file://$TEST_ROOT/shallow-clone\"; ref = \"dev\"; shallow = true; }).revCount or 123") == 123 ]] + +# Test 3: Check unlocked input error message +expectStderr 1 nix eval --expr 'builtins.fetchTree { type = "git"; url = "file:///foo"; }' | grepQuiet "'fetchTree' will not fetch unlocked input" + +# Test 4: Regression test for revCount in worktrees derived from shallow clones +# Add a worktree to the shallow clone +git -C "$TEST_ROOT/shallow-clone" worktree add "$TEST_ROOT/shallow-worktree" + +# Prior to the fix, this would error out because of the shallow clone's +# inability to find parent commits. Now it should return an error. +if nix eval --impure --expr "(builtins.fetchGit { url = \"file://$TEST_ROOT/shallow-worktree\"; }).revCount" 2>/dev/null; then + echo "fetchGit unexpectedly succeeded on shallow clone" >&2 + exit 1 +fi + +# Also verify that fetchTree fails similarly +if nix eval --impure --expr "(builtins.fetchTree { type = \"git\"; url = \"file://$TEST_ROOT/shallow-worktree\"; }).revCount" 2>/dev/null; then + echo "fetchTree unexpectedly succeeded on shallow clone" >&2 + exit 1 +fi + +# Verify that we can shallow fetch the worktree +git -C "$TEST_ROOT/shallow-worktree" rev-list --count HEAD >/dev/null +nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$TEST_ROOT/shallow-worktree\"; shallow = true; }).rev" diff --git a/tests/functional/meson.build b/tests/functional/meson.build index f5a19ac64..cd1bc6319 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -73,6 +73,7 @@ suites = [ 'gc-runtime.sh', 'tarball.sh', 'fetchGit.sh', + 'fetchGitShallow.sh', 'fetchurl.sh', 'fetchPath.sh', 'fetchTree-file.sh',