From e10dfd298385d615640e1bfb16b80a7a81aa989a Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Fri, 15 Nov 2019 13:04:42 +0100 Subject: [PATCH] builtins.fetchGit: Fix build when fetching a git worktree Worktrees[1] are a feature of git which allow you to check out a ref in a different directory. While playing around with flakes I realized that git repositories in a worktree checkout break when trying to build a flake: ``` $ git worktree add ../nixpkgs-flakes nixpkgs-flakes $ cd ../nixpkgs-flakes $ nix build .#hello error: opening directory '/home/ma27/Projects/nixpkgs-flakes/.git/refs/heads': Not a directory ``` This issue has been fixed by determining with `git rev-parse --git-common-dir` where the actual `.git` directory is. Please note that this issue only exists on the `flakes` branch, fetching worktree checkouts with Nix master seems to work fine. [1] https://git-scm.com/docs/git-worktree --- src/libexpr/primops/fetchGit.cc | 19 +++++++++++++++++-- tests/fetchGit.sh | 6 ++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/libexpr/primops/fetchGit.cc b/src/libexpr/primops/fetchGit.cc index 6f67e3d76311..bbd030d0fe05 100644 --- a/src/libexpr/primops/fetchGit.cc +++ b/src/libexpr/primops/fetchGit.cc @@ -92,7 +92,22 @@ GitInfo exportGit(ref store, std::string uri, if (hasPrefix(uri, "git+")) uri = std::string(uri, 4); - bool isLocal = hasPrefix(uri, "/") && pathExists(uri + "/.git"); + bool isLocalPath = hasPrefix(uri, "/"); + auto gitDir = uri + "/.git"; + if (isLocalPath) { + auto commonDir = chomp(runProgram( + "git", + true, + { "-C", uri, "rev-parse", "--git-common-dir" } + )); + + // if URI is not in a worktree, `git rev-parse --git-common-dir` always + // returns `.git`, even if `-C` is set :( + if (commonDir != ".git") + gitDir = commonDir; + } + + bool isLocal = isLocalPath && pathExists(gitDir); // If this is a local directory (but not a file:// URI) and no ref // or revision is given, then allow the use of an unclean working @@ -102,7 +117,7 @@ GitInfo exportGit(ref store, std::string uri, /* Check whether this repo has any commits. There are probably better ways to do this. */ - bool haveCommits = !readDirectory(uri + "/.git/refs/heads").empty(); + bool haveCommits = !readDirectory(gitDir + "/refs/heads").empty(); try { if (haveCommits) { diff --git a/tests/fetchGit.sh b/tests/fetchGit.sh index 885dd9e777f7..7906694707a3 100644 --- a/tests/fetchGit.sh +++ b/tests/fetchGit.sh @@ -25,8 +25,14 @@ rev1=$(git -C $repo rev-parse HEAD) echo world > $repo/hello git -C $repo commit -m 'Bla2' -a +git -C $repo worktree add $TEST_ROOT/worktree +echo hello >> $TEST_ROOT/worktree/hello rev2=$(git -C $repo rev-parse HEAD) +# Fetch a dirty worktree +path0=$(nix eval --impure --raw "(builtins.fetchGit $TEST_ROOT/worktree).outPath") +[[ $(tail -n 1 $path0/hello) = "hello" ]] + # Fetch the default branch. path=$(nix eval --impure --raw "(builtins.fetchGit file://$repo).outPath") [[ $(cat $path/hello) = world ]]