at 24.05-pre 30 kB view raw
1/* 2 <!-- This anchor is here for backwards compatibity --> 3 []{#sec-fileset} 4 5 The [`lib.fileset`](#sec-functions-library-fileset) library allows you to work with _file sets_. 6 A file set is a (mathematical) set of local files that can be added to the Nix store for use in Nix derivations. 7 File sets are easy and safe to use, providing obvious and composable semantics with good error messages to prevent mistakes. 8 9 ## Overview {#sec-fileset-overview} 10 11 Basics: 12 - [Implicit coercion from paths to file sets](#sec-fileset-path-coercion) 13 14 - [`lib.fileset.trace`](#function-library-lib.fileset.trace)/[`lib.fileset.traceVal`](#function-library-lib.fileset.trace): 15 16 Pretty-print file sets for debugging. 17 18 - [`lib.fileset.toSource`](#function-library-lib.fileset.toSource): 19 20 Add files in file sets to the store to use as derivation sources. 21 22 Combinators: 23 - [`lib.fileset.union`](#function-library-lib.fileset.union)/[`lib.fileset.unions`](#function-library-lib.fileset.unions): 24 25 Create a larger file set from all the files in multiple file sets. 26 27 - [`lib.fileset.intersection`](#function-library-lib.fileset.intersection): 28 29 Create a smaller file set from only the files in both file sets. 30 31 - [`lib.fileset.difference`](#function-library-lib.fileset.difference): 32 33 Create a smaller file set containing all files that are in one file set, but not another one. 34 35 Filtering: 36 - [`lib.fileset.fileFilter`](#function-library-lib.fileset.fileFilter): 37 38 Create a file set from all files that satisisfy a predicate in a directory. 39 40 Utilities: 41 - [`lib.fileset.fromSource`](#function-library-lib.fileset.fromSource): 42 43 Create a file set from a `lib.sources`-based value. 44 45 - [`lib.fileset.gitTracked`](#function-library-lib.fileset.gitTracked)/[`lib.fileset.gitTrackedWith`](#function-library-lib.fileset.gitTrackedWith): 46 47 Create a file set from all tracked files in a local Git repository. 48 49 If you need more file set functions, 50 see [this issue](https://github.com/NixOS/nixpkgs/issues/266356) to request it. 51 52 53 ## Implicit coercion from paths to file sets {#sec-fileset-path-coercion} 54 55 All functions accepting file sets as arguments can also accept [paths](https://nixos.org/manual/nix/stable/language/values.html#type-path) as arguments. 56 Such path arguments are implicitly coerced to file sets containing all files under that path: 57 - A path to a file turns into a file set containing that single file. 58 - A path to a directory turns into a file set containing all files _recursively_ in that directory. 59 60 If the path points to a non-existent location, an error is thrown. 61 62 ::: {.note} 63 Just like in Git, file sets cannot represent empty directories. 64 Because of this, a path to a directory that contains no files (recursively) will turn into a file set containing no files. 65 ::: 66 67 :::{.note} 68 File set coercion does _not_ add any of the files under the coerced paths to the store. 69 Only the [`toSource`](#function-library-lib.fileset.toSource) function adds files to the Nix store, and only those files contained in the `fileset` argument. 70 This is in contrast to using [paths in string interpolation](https://nixos.org/manual/nix/stable/language/values.html#type-path), which does add the entire referenced path to the store. 71 ::: 72 73 ### Example {#sec-fileset-path-coercion-example} 74 75 Assume we are in a local directory with a file hierarchy like this: 76 ``` 77 a/ 78 x (file) 79 b/ 80   y (file) 81 c/ 82    d/ 83 ``` 84 85 Here's a listing of which files get included when different path expressions get coerced to file sets: 86 - `./.` as a file set contains both `a/x` and `a/b/y` (`c/` does not contain any files and is therefore omitted). 87 - `./a` as a file set contains both `a/x` and `a/b/y`. 88 - `./a/x` as a file set contains only `a/x`. 89 - `./a/b` as a file set contains only `a/b/y`. 90 - `./c` as a file set is empty, since neither `c` nor `c/d` contain any files. 91*/ 92{ lib }: 93let 94 95 inherit (import ./internal.nix { inherit lib; }) 96 _coerce 97 _singleton 98 _coerceMany 99 _toSourceFilter 100 _fromSourceFilter 101 _unionMany 102 _fileFilter 103 _printFileset 104 _intersection 105 _difference 106 _mirrorStorePath 107 _fetchGitSubmodulesMinver 108 ; 109 110 inherit (builtins) 111 isBool 112 isList 113 isPath 114 pathExists 115 seq 116 typeOf 117 nixVersion 118 ; 119 120 inherit (lib.lists) 121 elemAt 122 imap0 123 ; 124 125 inherit (lib.path) 126 hasPrefix 127 splitRoot 128 ; 129 130 inherit (lib.strings) 131 isStringLike 132 versionOlder 133 ; 134 135 inherit (lib.filesystem) 136 pathType 137 ; 138 139 inherit (lib.sources) 140 cleanSourceWith 141 ; 142 143 inherit (lib.trivial) 144 isFunction 145 pipe 146 inPureEvalMode 147 ; 148 149in { 150 151 /* 152 Incrementally evaluate and trace a file set in a pretty way. 153 This function is only intended for debugging purposes. 154 The exact tracing format is unspecified and may change. 155 156 This function takes a final argument to return. 157 In comparison, [`traceVal`](#function-library-lib.fileset.traceVal) returns 158 the given file set argument. 159 160 This variant is useful for tracing file sets in the Nix repl. 161 162 Type: 163 trace :: FileSet -> Any -> Any 164 165 Example: 166 trace (unions [ ./Makefile ./src ./tests/run.sh ]) null 167 => 168 trace: /home/user/src/myProject 169 trace: - Makefile (regular) 170 trace: - src (all files in directory) 171 trace: - tests 172 trace: - run.sh (regular) 173 null 174 */ 175 trace = 176 /* 177 The file set to trace. 178 179 This argument can also be a path, 180 which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). 181 */ 182 fileset: 183 let 184 # "fileset" would be a better name, but that would clash with the argument name, 185 # and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76 186 actualFileset = _coerce "lib.fileset.trace: Argument" fileset; 187 in 188 seq 189 (_printFileset actualFileset) 190 (x: x); 191 192 /* 193 Incrementally evaluate and trace a file set in a pretty way. 194 This function is only intended for debugging purposes. 195 The exact tracing format is unspecified and may change. 196 197 This function returns the given file set. 198 In comparison, [`trace`](#function-library-lib.fileset.trace) takes another argument to return. 199 200 This variant is useful for tracing file sets passed as arguments to other functions. 201 202 Type: 203 traceVal :: FileSet -> FileSet 204 205 Example: 206 toSource { 207 root = ./.; 208 fileset = traceVal (unions [ 209 ./Makefile 210 ./src 211 ./tests/run.sh 212 ]); 213 } 214 => 215 trace: /home/user/src/myProject 216 trace: - Makefile (regular) 217 trace: - src (all files in directory) 218 trace: - tests 219 trace: - run.sh (regular) 220 "/nix/store/...-source" 221 */ 222 traceVal = 223 /* 224 The file set to trace and return. 225 226 This argument can also be a path, 227 which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). 228 */ 229 fileset: 230 let 231 # "fileset" would be a better name, but that would clash with the argument name, 232 # and we cannot change that because of https://github.com/nix-community/nixdoc/issues/76 233 actualFileset = _coerce "lib.fileset.traceVal: Argument" fileset; 234 in 235 seq 236 (_printFileset actualFileset) 237 # We could also return the original fileset argument here, 238 # but that would then duplicate work for consumers of the fileset, because then they have to coerce it again 239 actualFileset; 240 241 /* 242 Add the local files contained in `fileset` to the store as a single [store path](https://nixos.org/manual/nix/stable/glossary#gloss-store-path) rooted at `root`. 243 244 The result is the store path as a string-like value, making it usable e.g. as the `src` of a derivation, or in string interpolation: 245 ```nix 246 stdenv.mkDerivation { 247 src = lib.fileset.toSource { ... }; 248 # ... 249 } 250 ``` 251 252 The name of the store path is always `source`. 253 254 Type: 255 toSource :: { 256 root :: Path, 257 fileset :: FileSet, 258 } -> SourceLike 259 260 Example: 261 # Import the current directory into the store 262 # but only include files under ./src 263 toSource { 264 root = ./.; 265 fileset = ./src; 266 } 267 => "/nix/store/...-source" 268 269 # Import the current directory into the store 270 # but only include ./Makefile and all files under ./src 271 toSource { 272 root = ./.; 273 fileset = union 274 ./Makefile 275 ./src; 276 } 277 => "/nix/store/...-source" 278 279 # Trying to include a file outside the root will fail 280 toSource { 281 root = ./.; 282 fileset = unions [ 283 ./Makefile 284 ./src 285 ../LICENSE 286 ]; 287 } 288 => <error> 289 290 # The root needs to point to a directory that contains all the files 291 toSource { 292 root = ../.; 293 fileset = unions [ 294 ./Makefile 295 ./src 296 ../LICENSE 297 ]; 298 } 299 => "/nix/store/...-source" 300 301 # The root has to be a local filesystem path 302 toSource { 303 root = "/nix/store/...-source"; 304 fileset = ./.; 305 } 306 => <error> 307 */ 308 toSource = { 309 /* 310 (required) The local directory [path](https://nixos.org/manual/nix/stable/language/values.html#type-path) that will correspond to the root of the resulting store path. 311 Paths in [strings](https://nixos.org/manual/nix/stable/language/values.html#type-string), including Nix store paths, cannot be passed as `root`. 312 `root` has to be a directory. 313 314 :::{.note} 315 Changing `root` only affects the directory structure of the resulting store path, it does not change which files are added to the store. 316 The only way to change which files get added to the store is by changing the `fileset` attribute. 317 ::: 318 */ 319 root, 320 /* 321 (required) The file set whose files to import into the store. 322 File sets can be created using other functions in this library. 323 This argument can also be a path, 324 which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). 325 326 :::{.note} 327 If a directory does not recursively contain any file, it is omitted from the store path contents. 328 ::: 329 330 */ 331 fileset, 332 }: 333 let 334 # We cannot rename matched attribute arguments, so let's work around it with an extra `let in` statement 335 filesetArg = fileset; 336 in 337 let 338 fileset = _coerce "lib.fileset.toSource: `fileset`" filesetArg; 339 rootFilesystemRoot = (splitRoot root).root; 340 filesetFilesystemRoot = (splitRoot fileset._internalBase).root; 341 sourceFilter = _toSourceFilter fileset; 342 in 343 if ! isPath root then 344 if root ? _isLibCleanSourceWith then 345 throw '' 346 lib.fileset.toSource: `root` is a `lib.sources`-based value, but it should be a path instead. 347 To use a `lib.sources`-based value, convert it to a file set using `lib.fileset.fromSource` and pass it as `fileset`. 348 Note that this only works for sources created from paths.'' 349 else if isStringLike root then 350 throw '' 351 lib.fileset.toSource: `root` (${toString root}) is a string-like value, but it should be a path instead. 352 Paths in strings are not supported by `lib.fileset`, use `lib.sources` or derivations instead.'' 353 else 354 throw '' 355 lib.fileset.toSource: `root` is of type ${typeOf root}, but it should be a path instead.'' 356 # Currently all Nix paths have the same filesystem root, but this could change in the future. 357 # See also ../path/README.md 358 else if ! fileset._internalIsEmptyWithoutBase && rootFilesystemRoot != filesetFilesystemRoot then 359 throw '' 360 lib.fileset.toSource: Filesystem roots are not the same for `fileset` and `root` (${toString root}): 361 `root`: Filesystem root is "${toString rootFilesystemRoot}" 362 `fileset`: Filesystem root is "${toString filesetFilesystemRoot}" 363 Different filesystem roots are not supported.'' 364 else if ! pathExists root then 365 throw '' 366 lib.fileset.toSource: `root` (${toString root}) is a path that does not exist.'' 367 else if pathType root != "directory" then 368 throw '' 369 lib.fileset.toSource: `root` (${toString root}) is a file, but it should be a directory instead. Potential solutions: 370 - If you want to import the file into the store _without_ a containing directory, use string interpolation or `builtins.path` instead of this function. 371 - If you want to import the file into the store _with_ a containing directory, set `root` to the containing directory, such as ${toString (dirOf root)}, and set `fileset` to the file path.'' 372 else if ! fileset._internalIsEmptyWithoutBase && ! hasPrefix root fileset._internalBase then 373 throw '' 374 lib.fileset.toSource: `fileset` could contain files in ${toString fileset._internalBase}, which is not under the `root` (${toString root}). Potential solutions: 375 - Set `root` to ${toString fileset._internalBase} or any directory higher up. This changes the layout of the resulting store path. 376 - Set `fileset` to a file set that cannot contain files outside the `root` (${toString root}). This could change the files included in the result.'' 377 else 378 seq sourceFilter 379 cleanSourceWith { 380 name = "source"; 381 src = root; 382 filter = sourceFilter; 383 }; 384 385 /* 386 The file set containing all files that are in either of two given file sets. 387 This is the same as [`unions`](#function-library-lib.fileset.unions), 388 but takes just two file sets instead of a list. 389 See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)). 390 391 The given file sets are evaluated as lazily as possible, 392 with the first argument being evaluated first if needed. 393 394 Type: 395 union :: FileSet -> FileSet -> FileSet 396 397 Example: 398 # Create a file set containing the file `Makefile` 399 # and all files recursively in the `src` directory 400 union ./Makefile ./src 401 402 # Create a file set containing the file `Makefile` 403 # and the LICENSE file from the parent directory 404 union ./Makefile ../LICENSE 405 */ 406 union = 407 # The first file set. 408 # This argument can also be a path, 409 # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). 410 fileset1: 411 # The second file set. 412 # This argument can also be a path, 413 # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). 414 fileset2: 415 _unionMany 416 (_coerceMany "lib.fileset.union" [ 417 { 418 context = "First argument"; 419 value = fileset1; 420 } 421 { 422 context = "Second argument"; 423 value = fileset2; 424 } 425 ]); 426 427 /* 428 The file set containing all files that are in any of the given file sets. 429 This is the same as [`union`](#function-library-lib.fileset.unions), 430 but takes a list of file sets instead of just two. 431 See also [Union (set theory)](https://en.wikipedia.org/wiki/Union_(set_theory)). 432 433 The given file sets are evaluated as lazily as possible, 434 with earlier elements being evaluated first if needed. 435 436 Type: 437 unions :: [ FileSet ] -> FileSet 438 439 Example: 440 # Create a file set containing selected files 441 unions [ 442 # Include the single file `Makefile` in the current directory 443 # This errors if the file doesn't exist 444 ./Makefile 445 446 # Recursively include all files in the `src/code` directory 447 # If this directory is empty this has no effect 448 ./src/code 449 450 # Include the files `run.sh` and `unit.c` from the `tests` directory 451 ./tests/run.sh 452 ./tests/unit.c 453 454 # Include the `LICENSE` file from the parent directory 455 ../LICENSE 456 ] 457 */ 458 unions = 459 # A list of file sets. 460 # The elements can also be paths, 461 # which get [implicitly coerced to file sets](#sec-fileset-path-coercion). 462 filesets: 463 if ! isList filesets then 464 throw '' 465 lib.fileset.unions: Argument is of type ${typeOf filesets}, but it should be a list instead.'' 466 else 467 pipe filesets [ 468 # Annotate the elements with context, used by _coerceMany for better errors 469 (imap0 (i: el: { 470 context = "Element ${toString i}"; 471 value = el; 472 })) 473 (_coerceMany "lib.fileset.unions") 474 _unionMany 475 ]; 476 477 /* 478 The file set containing all files that are in both of two given file sets. 479 See also [Intersection (set theory)](https://en.wikipedia.org/wiki/Intersection_(set_theory)). 480 481 The given file sets are evaluated as lazily as possible, 482 with the first argument being evaluated first if needed. 483 484 Type: 485 intersection :: FileSet -> FileSet -> FileSet 486 487 Example: 488 # Limit the selected files to the ones in ./., so only ./src and ./Makefile 489 intersection ./. (unions [ ../LICENSE ./src ./Makefile ]) 490 */ 491 intersection = 492 # The first file set. 493 # This argument can also be a path, 494 # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). 495 fileset1: 496 # The second file set. 497 # This argument can also be a path, 498 # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). 499 fileset2: 500 let 501 filesets = _coerceMany "lib.fileset.intersection" [ 502 { 503 context = "First argument"; 504 value = fileset1; 505 } 506 { 507 context = "Second argument"; 508 value = fileset2; 509 } 510 ]; 511 in 512 _intersection 513 (elemAt filesets 0) 514 (elemAt filesets 1); 515 516 /* 517 The file set containing all files from the first file set that are not in the second file set. 518 See also [Difference (set theory)](https://en.wikipedia.org/wiki/Complement_(set_theory)#Relative_complement). 519 520 The given file sets are evaluated as lazily as possible, 521 with the first argument being evaluated first if needed. 522 523 Type: 524 union :: FileSet -> FileSet -> FileSet 525 526 Example: 527 # Create a file set containing all files from the current directory, 528 # except ones under ./tests 529 difference ./. ./tests 530 531 let 532 # A set of Nix-related files 533 nixFiles = unions [ ./default.nix ./nix ./tests/default.nix ]; 534 in 535 # Create a file set containing all files under ./tests, except ones in `nixFiles`, 536 # meaning only without ./tests/default.nix 537 difference ./tests nixFiles 538 */ 539 difference = 540 # The positive file set. 541 # The result can only contain files that are also in this file set. 542 # 543 # This argument can also be a path, 544 # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). 545 positive: 546 # The negative file set. 547 # The result will never contain files that are also in this file set. 548 # 549 # This argument can also be a path, 550 # which gets [implicitly coerced to a file set](#sec-fileset-path-coercion). 551 negative: 552 let 553 filesets = _coerceMany "lib.fileset.difference" [ 554 { 555 context = "First argument (positive set)"; 556 value = positive; 557 } 558 { 559 context = "Second argument (negative set)"; 560 value = negative; 561 } 562 ]; 563 in 564 _difference 565 (elemAt filesets 0) 566 (elemAt filesets 1); 567 568 /* 569 Filter a file set to only contain files matching some predicate. 570 571 Type: 572 fileFilter :: 573 ({ 574 name :: String, 575 type :: String, 576 ... 577 } -> Bool) 578 -> Path 579 -> FileSet 580 581 Example: 582 # Include all regular `default.nix` files in the current directory 583 fileFilter (file: file.name == "default.nix") ./. 584 585 # Include all non-Nix files from the current directory 586 fileFilter (file: ! hasSuffix ".nix" file.name) ./. 587 588 # Include all files that start with a "." in the current directory 589 fileFilter (file: hasPrefix "." file.name) ./. 590 591 # Include all regular files (not symlinks or others) in the current directory 592 fileFilter (file: file.type == "regular") ./. 593 */ 594 fileFilter = 595 /* 596 The predicate function to call on all files contained in given file set. 597 A file is included in the resulting file set if this function returns true for it. 598 599 This function is called with an attribute set containing these attributes: 600 601 - `name` (String): The name of the file 602 603 - `type` (String, one of `"regular"`, `"symlink"` or `"unknown"`): The type of the file. 604 This matches result of calling [`builtins.readFileType`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-readFileType) on the file's path. 605 606 Other attributes may be added in the future. 607 */ 608 predicate: 609 # The path whose files to filter 610 path: 611 if ! isFunction predicate then 612 throw '' 613 lib.fileset.fileFilter: First argument is of type ${typeOf predicate}, but it should be a function instead.'' 614 else if ! isPath path then 615 if path._type or "" == "fileset" then 616 throw '' 617 lib.fileset.fileFilter: Second argument is a file set, but it should be a path instead. 618 If you need to filter files in a file set, use `intersection fileset (fileFilter pred ./.)` instead.'' 619 else 620 throw '' 621 lib.fileset.fileFilter: Second argument is of type ${typeOf path}, but it should be a path instead.'' 622 else if ! pathExists path then 623 throw '' 624 lib.fileset.fileFilter: Second argument (${toString path}) is a path that does not exist.'' 625 else 626 _fileFilter predicate path; 627 628 /* 629 Create a file set with the same files as a `lib.sources`-based value. 630 This does not import any of the files into the store. 631 632 This can be used to gradually migrate from `lib.sources`-based filtering to `lib.fileset`. 633 634 A file set can be turned back into a source using [`toSource`](#function-library-lib.fileset.toSource). 635 636 :::{.note} 637 File sets cannot represent empty directories. 638 Turning the result of this function back into a source using `toSource` will therefore not preserve empty directories. 639 ::: 640 641 Type: 642 fromSource :: SourceLike -> FileSet 643 644 Example: 645 # There's no cleanSource-like function for file sets yet, 646 # but we can just convert cleanSource to a file set and use it that way 647 toSource { 648 root = ./.; 649 fileset = fromSource (lib.sources.cleanSource ./.); 650 } 651 652 # Keeping a previous sourceByRegex (which could be migrated to `lib.fileset.unions`), 653 # but removing a subdirectory using file set functions 654 difference 655 (fromSource (lib.sources.sourceByRegex ./. [ 656 "^README\.md$" 657 # This regex includes everything in ./doc 658 "^doc(/.*)?$" 659 ]) 660 ./doc/generated 661 662 # Use cleanSource, but limit it to only include ./Makefile and files under ./src 663 intersection 664 (fromSource (lib.sources.cleanSource ./.)) 665 (unions [ 666 ./Makefile 667 ./src 668 ]); 669 */ 670 fromSource = source: 671 let 672 # This function uses `._isLibCleanSourceWith`, `.origSrc` and `.filter`, 673 # which are technically internal to lib.sources, 674 # but we'll allow this since both libraries are in the same code base 675 # and this function is a bridge between them. 676 isFiltered = source ? _isLibCleanSourceWith; 677 path = if isFiltered then source.origSrc else source; 678 in 679 # We can only support sources created from paths 680 if ! isPath path then 681 if isStringLike path then 682 throw '' 683 lib.fileset.fromSource: The source origin of the argument is a string-like value ("${toString path}"), but it should be a path instead. 684 Sources created from paths in strings cannot be turned into file sets, use `lib.sources` or derivations instead.'' 685 else 686 throw '' 687 lib.fileset.fromSource: The source origin of the argument is of type ${typeOf path}, but it should be a path instead.'' 688 else if ! pathExists path then 689 throw '' 690 lib.fileset.fromSource: The source origin (${toString path}) of the argument is a path that does not exist.'' 691 else if isFiltered then 692 _fromSourceFilter path source.filter 693 else 694 # If there's no filter, no need to run the expensive conversion, all subpaths will be included 695 _singleton path; 696 697 /* 698 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. 699 700 This function behaves like [`gitTrackedWith { }`](#function-library-lib.fileset.gitTrackedWith) - using the defaults. 701 702 Type: 703 gitTracked :: Path -> FileSet 704 705 Example: 706 # Include all files tracked by the Git repository in the current directory 707 gitTracked ./. 708 709 # Include only files tracked by the Git repository in the parent directory 710 # that are also in the current directory 711 intersection ./. (gitTracked ../.) 712 */ 713 gitTracked = 714 /* 715 The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository. 716 This directory must contain a `.git` file or subdirectory. 717 */ 718 path: 719 # See the gitTrackedWith implementation for more explanatory comments 720 let 721 fetchResult = builtins.fetchGit path; 722 in 723 if inPureEvalMode then 724 throw "lib.fileset.gitTracked: This function is currently not supported in pure evaluation mode, since it currently relies on `builtins.fetchGit`. See https://github.com/NixOS/nix/issues/9292." 725 else if ! isPath path then 726 throw "lib.fileset.gitTracked: Expected the argument to be a path, but it's a ${typeOf path} instead." 727 else if ! pathExists (path + "/.git") then 728 throw "lib.fileset.gitTracked: Expected the argument (${toString path}) to point to a local working tree of a Git repository, but it's not." 729 else 730 _mirrorStorePath path fetchResult.outPath; 731 732 /* 733 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. 734 The first argument allows configuration with an attribute set, 735 while the second argument is the path to the Git working tree. 736 If you don't need the configuration, 737 you can use [`gitTracked`](#function-library-lib.fileset.gitTracked) instead. 738 739 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) 740 (which uses [`--cached`](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt--c) by default). 741 742 :::{.warning} 743 Currently this function is based on [`builtins.fetchGit`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-fetchGit) 744 As such, this function causes all Git-tracked files to be unnecessarily added to the Nix store, 745 without being re-usable by [`toSource`](#function-library-lib.fileset.toSource). 746 747 This may change in the future. 748 ::: 749 750 Type: 751 gitTrackedWith :: { recurseSubmodules :: Bool ? false } -> Path -> FileSet 752 753 Example: 754 # Include all files tracked by the Git repository in the current directory 755 # and any submodules under it 756 gitTracked { recurseSubmodules = true; } ./. 757 */ 758 gitTrackedWith = 759 { 760 /* 761 (optional, default: `false`) Whether to recurse into [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to also include their tracked files. 762 763 If `true`, this is equivalent to passing the [--recurse-submodules](https://git-scm.com/docs/git-ls-files#Documentation/git-ls-files.txt---recurse-submodules) flag to `git ls-files`. 764 */ 765 recurseSubmodules ? false, 766 }: 767 /* 768 The [path](https://nixos.org/manual/nix/stable/language/values#type-path) to the working directory of a local Git repository. 769 This directory must contain a `.git` file or subdirectory. 770 */ 771 path: 772 let 773 # This imports the files unnecessarily, which currently can't be avoided 774 # because `builtins.fetchGit` is the only function exposing which files are tracked by Git. 775 # With the [lazy trees PR](https://github.com/NixOS/nix/pull/6530), 776 # the unnecessarily import could be avoided. 777 # However a simpler alternative still would be [a builtins.gitLsFiles](https://github.com/NixOS/nix/issues/2944). 778 fetchResult = builtins.fetchGit { 779 url = path; 780 781 # This is the only `fetchGit` parameter that makes sense in this context. 782 # We can't just pass `submodules = recurseSubmodules` here because 783 # this would fail for Nix versions that don't support `submodules`. 784 ${if recurseSubmodules then "submodules" else null} = true; 785 }; 786 in 787 if inPureEvalMode then 788 throw "lib.fileset.gitTrackedWith: This function is currently not supported in pure evaluation mode, since it currently relies on `builtins.fetchGit`. See https://github.com/NixOS/nix/issues/9292." 789 else if ! isBool recurseSubmodules then 790 throw "lib.fileset.gitTrackedWith: Expected the attribute `recurseSubmodules` of the first argument to be a boolean, but it's a ${typeOf recurseSubmodules} instead." 791 else if recurseSubmodules && versionOlder nixVersion _fetchGitSubmodulesMinver then 792 throw "lib.fileset.gitTrackedWith: Setting the attribute `recurseSubmodules` to `true` is only supported for Nix version ${_fetchGitSubmodulesMinver} and after, but Nix version ${nixVersion} is used." 793 else if ! isPath path then 794 throw "lib.fileset.gitTrackedWith: Expected the second argument to be a path, but it's a ${typeOf path} instead." 795 # We can identify local working directories by checking for .git, 796 # see https://git-scm.com/docs/gitrepository-layout#_description. 797 # Note that `builtins.fetchGit` _does_ work for bare repositories (where there's no `.git`), 798 # even though `git ls-files` wouldn't return any files in that case. 799 else if ! pathExists (path + "/.git") then 800 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." 801 else 802 _mirrorStorePath path fetchResult.outPath; 803}