lib.fileset.gitTracked/gitTrackedWith: init
A configuration parameter for gitTrackedWith will be introduced in the next commit
This commit is contained in:
parent
91c993afb9
commit
2dfb1d36cf
@ -12,6 +12,7 @@ let
|
||||
_printFileset
|
||||
_intersection
|
||||
_difference
|
||||
_mirrorStorePath
|
||||
;
|
||||
|
||||
inherit (builtins)
|
||||
@ -596,4 +597,91 @@ in {
|
||||
# We could also return the original fileset argument here,
|
||||
# but that would then duplicate work for consumers of the fileset, because then they have to coerce it again
|
||||
actualFileset;
|
||||
|
||||
/*
|
||||
Create a file set containing all [Git-tracked files](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) in a repository.
|
||||
|
||||
This function behaves like [`gitTrackedWith { }`](#function-library-lib.fileset.gitTrackedWith) - using the defaults.
|
||||
|
||||
Type:
|
||||
gitTracked :: Path -> FileSet
|
||||
|
||||
Example:
|
||||
# Include all files tracked by the Git repository in the current directory
|
||||
gitTracked ./.
|
||||
|
||||
# Include only files tracked by the Git repository in the parent directory
|
||||
# that are also in the current directory
|
||||
intersection ./. (gitTracked ../.)
|
||||
*/
|
||||
gitTracked =
|
||||
/*
|
||||
The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository.
|
||||
This directory must contain a `.git` file or subdirectory.
|
||||
*/
|
||||
path:
|
||||
# See the gitTrackedWith implementation for more explanatory comments
|
||||
let
|
||||
fetchResult = builtins.fetchGit path;
|
||||
in
|
||||
if ! isPath path then
|
||||
throw "lib.fileset.gitTracked: Expected the argument to be a path, but it's a ${typeOf path} instead."
|
||||
else if ! pathExists (path + "/.git") then
|
||||
throw "lib.fileset.gitTracked: Expected the argument (${toString path}) to point to a local working tree of a Git repository, but it's not."
|
||||
else
|
||||
_mirrorStorePath path fetchResult.outPath;
|
||||
|
||||
/*
|
||||
Create a file set containing all [Git-tracked files](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) in a repository.
|
||||
The first argument allows configuration with an attribute set,
|
||||
while the second argument is the path to the Git working tree.
|
||||
If you don't need the configuration,
|
||||
you can use [`gitTracked`](#function-library-lib.fileset.gitTracked) instead.
|
||||
|
||||
This is equivalent to the result of [`unions`](#function-library-lib.fileset.unions) on all files returned by [`git ls-files`](https://git-scm.com/docs/git-ls-files)
|
||||
(which uses [`--cached`](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt--c) by default).
|
||||
|
||||
:::{.warning}
|
||||
Currently this function is based on [`builtins.fetchGit`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-fetchGit)
|
||||
As such, this function causes all Git-tracked files to be unnecessarily added to the Nix store,
|
||||
without being re-usable by [`toSource`](#function-library-lib.fileset.toSource).
|
||||
|
||||
This may change in the future.
|
||||
:::
|
||||
|
||||
Type:
|
||||
gitTrackedWith :: { } -> Path -> FileSet
|
||||
|
||||
Example:
|
||||
# Include all files tracked by the Git repository in the current directory
|
||||
gitTracked { } ./.
|
||||
*/
|
||||
gitTrackedWith =
|
||||
{
|
||||
}:
|
||||
/*
|
||||
The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository.
|
||||
This directory must contain a `.git` file or subdirectory.
|
||||
*/
|
||||
path:
|
||||
let
|
||||
# This imports the files unnecessarily, which currently can't be avoided
|
||||
# because `builtins.fetchGit` is the only function exposing which files are tracked by Git.
|
||||
# With the [lazy trees PR](https://github.com/NixOS/nix/pull/6530),
|
||||
# the unnecessarily import could be avoided.
|
||||
# However a simpler alternative still would be [a builtins.gitLsFiles](https://github.com/NixOS/nix/issues/2944).
|
||||
fetchResult = builtins.fetchGit {
|
||||
url = path;
|
||||
};
|
||||
in
|
||||
if ! isPath path then
|
||||
throw "lib.fileset.gitTrackedWith: Expected the second argument to be a path, but it's a ${typeOf path} instead."
|
||||
# We can identify local working directories by checking for .git,
|
||||
# see https://git-scm.com/docs/gitrepository-layout#_description.
|
||||
# Note that `builtins.fetchGit` _does_ work for bare repositories (where there's no `.git`),
|
||||
# even though `git ls-files` wouldn't return any files in that case.
|
||||
else if ! pathExists (path + "/.git") then
|
||||
throw "lib.fileset.gitTrackedWith: Expected the second argument (${toString path}) to point to a local working tree of a Git repository, but it's not."
|
||||
else
|
||||
_mirrorStorePath path fetchResult.outPath;
|
||||
}
|
||||
|
@ -825,4 +825,23 @@ rec {
|
||||
${baseNameOf root} =
|
||||
fromFile (baseNameOf root) rootType;
|
||||
};
|
||||
|
||||
# Mirrors the contents of a Nix store path relative to a local path as a file set.
|
||||
# Some notes:
|
||||
# - The store path is read at evaluation time.
|
||||
# - The store path must not include files that don't exist in the respective local path.
|
||||
#
|
||||
# Type: Path -> String -> FileSet
|
||||
_mirrorStorePath = localPath: storePath:
|
||||
let
|
||||
recurse = focusedStorePath:
|
||||
mapAttrs (name: type:
|
||||
if type == "directory" then
|
||||
recurse (focusedStorePath + "/${name}")
|
||||
else
|
||||
type
|
||||
) (builtins.readDir focusedStorePath);
|
||||
in
|
||||
_create localPath
|
||||
(recurse storePath);
|
||||
}
|
||||
|
@ -95,8 +95,9 @@ expectEqual() {
|
||||
# Usage: expectStorePath NIX
|
||||
expectStorePath() {
|
||||
local expr=$1
|
||||
if ! result=$(nix-instantiate --eval --strict --json --read-write-mode --show-trace \
|
||||
if ! result=$(nix-instantiate --eval --strict --json --read-write-mode --show-trace 2>"$tmp"/stderr \
|
||||
--expr "$prefixExpression ($expr)"); then
|
||||
cat "$tmp/stderr" >&2
|
||||
die "$expr failed to evaluate, but it was expected to succeed"
|
||||
fi
|
||||
# This is safe because we assume to get back a store path in a string
|
||||
@ -1251,6 +1252,150 @@ expectEqual 'trace (intersection ./a (fromSource (lib.cleanSourceWith {
|
||||
}))) null' 'trace ./a/b null'
|
||||
rm -rf -- *
|
||||
|
||||
## lib.fileset.gitTracked/gitTrackedWith
|
||||
|
||||
# The first/second argument has to be a path
|
||||
expectFailure 'gitTracked null' 'lib.fileset.gitTracked: Expected the argument to be a path, but it'\''s a null instead.'
|
||||
expectFailure 'gitTrackedWith {} null' 'lib.fileset.gitTrackedWith: Expected the second argument to be a path, but it'\''s a null instead.'
|
||||
|
||||
# The path has to contain a .git directory
|
||||
expectFailure 'gitTracked ./.' 'lib.fileset.gitTracked: Expected the argument \('"$work"'\) to point to a local working tree of a Git repository, but it'\''s not.'
|
||||
expectFailure 'gitTrackedWith {} ./.' 'lib.fileset.gitTrackedWith: Expected the second argument \('"$work"'\) to point to a local working tree of a Git repository, but it'\''s not.'
|
||||
|
||||
# Checks that `gitTrackedWith` contains the same files as `git ls-files`
|
||||
# for the current working directory.
|
||||
# If --recurse-submodules is passed, the flag is passed through to `git ls-files`
|
||||
# and as `recurseSubmodules` to `gitTrackedWith`
|
||||
checkGitTrackedWith() {
|
||||
# All files listed by `git ls-files`
|
||||
expectedFiles=()
|
||||
while IFS= read -r -d $'\0' file; do
|
||||
# If there are submodules but --recurse-submodules isn't passed,
|
||||
# `git ls-files` lists them as empty directories,
|
||||
# we need to filter that out since we only want to check/count files
|
||||
if [[ -f "$file" ]]; then
|
||||
expectedFiles+=("$file")
|
||||
fi
|
||||
done < <(git ls-files -z)
|
||||
|
||||
storePath=$(expectStorePath 'toSource { root = ./.; fileset = gitTrackedWith { } ./.; }')
|
||||
|
||||
# Check that each expected file is also in the store path with the same content
|
||||
for expectedFile in "${expectedFiles[@]}"; do
|
||||
if [[ ! -e "$storePath"/"$expectedFile" ]]; then
|
||||
die "Expected file $expectedFile to exist in $storePath, but it doesn't.\nGit status:\n$(git status)\nStore path contents:\n$(find "$storePath")"
|
||||
fi
|
||||
if ! diff "$expectedFile" "$storePath"/"$expectedFile"; then
|
||||
die "Expected file $expectedFile to have the same contents as in $storePath, but it doesn't.\nGit status:\n$(git status)\nStore path contents:\n$(find "$storePath")"
|
||||
fi
|
||||
done
|
||||
|
||||
# This is a cheap way to verify the inverse: That all files in the store path are also expected
|
||||
# We just count the number of files in both and verify they're the same
|
||||
actualFileCount=$(find "$storePath" -type f -printf . | wc -c)
|
||||
if [[ "${#expectedFiles[@]}" != "$actualFileCount" ]]; then
|
||||
die "Expected ${#expectedFiles[@]} files in $storePath, but got $actualFileCount.\nGit status:\n$(git status)\nStore path contents:\n$(find "$storePath")"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Runs checkGitTrackedWith, this will make more sense in the next commit
|
||||
checkGitTracked() {
|
||||
checkGitTrackedWith
|
||||
}
|
||||
|
||||
createGitRepo() {
|
||||
git init -q "$1"
|
||||
# Only repo-local config
|
||||
git -C "$1" config user.name "Nixpkgs"
|
||||
git -C "$1" config user.email "nixpkgs@nixos.org"
|
||||
# Get at least a HEAD commit, needed for older Nix versions
|
||||
git -C "$1" commit -q --allow-empty -m "Empty commit"
|
||||
}
|
||||
|
||||
# Go through all stages of Git files
|
||||
# See https://www.git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository
|
||||
|
||||
# Empty repository
|
||||
createGitRepo .
|
||||
checkGitTracked
|
||||
|
||||
# Untracked file
|
||||
echo a > a
|
||||
checkGitTracked
|
||||
|
||||
# Staged file
|
||||
git add a
|
||||
checkGitTracked
|
||||
|
||||
# Committed file
|
||||
git commit -q -m "Added a"
|
||||
checkGitTracked
|
||||
|
||||
# Edited file
|
||||
echo b > a
|
||||
checkGitTracked
|
||||
|
||||
# Removed file
|
||||
git rm -f -q a
|
||||
checkGitTracked
|
||||
|
||||
rm -rf -- *
|
||||
|
||||
# gitignored file
|
||||
createGitRepo .
|
||||
echo a > .gitignore
|
||||
touch a
|
||||
git add -A
|
||||
checkGitTracked
|
||||
|
||||
# Add it regardless (needs -f)
|
||||
git add -f a
|
||||
checkGitTracked
|
||||
rm -rf -- *
|
||||
|
||||
# Directory
|
||||
createGitRepo .
|
||||
mkdir -p d1/d2/d3
|
||||
touch d1/d2/d3/a
|
||||
git add d1
|
||||
checkGitTracked
|
||||
rm -rf -- *
|
||||
|
||||
# Submodules
|
||||
createGitRepo .
|
||||
createGitRepo sub
|
||||
|
||||
# Untracked submodule
|
||||
git -C sub commit -q --allow-empty -m "Empty commit"
|
||||
checkGitTracked
|
||||
|
||||
# Tracked submodule
|
||||
git submodule add ./sub sub >/dev/null
|
||||
checkGitTracked
|
||||
|
||||
# Untracked file
|
||||
echo a > sub/a
|
||||
checkGitTracked
|
||||
|
||||
# Staged file
|
||||
git -C sub add a
|
||||
checkGitTracked
|
||||
|
||||
# Committed file
|
||||
git -C sub commit -q -m "Add a"
|
||||
checkGitTracked
|
||||
|
||||
# Changed file
|
||||
echo b > sub/b
|
||||
checkGitTracked
|
||||
|
||||
# Removed file
|
||||
git -C sub rm -f -q a
|
||||
checkGitTracked
|
||||
|
||||
rm -rf -- *
|
||||
|
||||
# TODO: Once we have combinators and a property testing library, derive property tests from https://en.wikipedia.org/wiki/Algebra_of_sets
|
||||
|
||||
echo >&2 tests ok
|
||||
|
@ -25,11 +25,13 @@ let
|
||||
];
|
||||
nativeBuildInputs = [
|
||||
nix
|
||||
pkgs.gitMinimal
|
||||
] ++ lib.optional pkgs.stdenv.isLinux pkgs.inotify-tools;
|
||||
strictDeps = true;
|
||||
} ''
|
||||
datadir="${nix}/share"
|
||||
export TEST_ROOT=$(pwd)/test-tmp
|
||||
export HOME=$(mktemp -d)
|
||||
export NIX_BUILD_HOOK=
|
||||
export NIX_CONF_DIR=$TEST_ROOT/etc
|
||||
export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
|
||||
|
Loading…
Reference in New Issue
Block a user