Skip to content

Commit

Permalink
builtins.fetchGit: Fix build when fetching a git worktree
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Ma27 committed Nov 15, 2019
1 parent 8d2eb1f commit e10dfd2
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 2 deletions.
19 changes: 17 additions & 2 deletions src/libexpr/primops/fetchGit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,22 @@ GitInfo exportGit(ref<Store> 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
Expand All @@ -102,7 +117,7 @@ GitInfo exportGit(ref<Store> 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) {
Expand Down
6 changes: 6 additions & 0 deletions tests/fetchGit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]]
Expand Down

0 comments on commit e10dfd2

Please sign in to comment.