-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgitSource.nix
104 lines (90 loc) · 3.22 KB
/
gitSource.nix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
{ pkgs
# Root of git project
, gitRootDir
# Optional extra filtering of the git-tracked files.
, extraFilter ? p: t: true
# If set then the result of `git ls-files` grepped with this string.
, grepFor ? ""
}:
# Based on Joachim Breitner's excellent example at
# https://github.com/NixOS/nix/issues/2944
# The function call
#
# gitSource ./toplevel subpath
#
# creates a Nix store path of ./toplevel/subpath that includes only those files
# tracked by git. More precisely: mentioned in the git index (i.e. git add is enough
# to get them to be included, you do not have to commit).
#
# This is a whitelist-based alternative to manually listing files or using
# nix-gitignore.
# Internally, it works by calling git ls-files at evaluation time. To
# avoid copying all of `.git` to the git store, it only copies the least amount
# of files necessary for `git ls-files` to work; this is a bit fragile, but
# very fast.
with builtins;
# We read the git index once, before getting the subdir parameter, so that it
# is shared among multiple invocations of gitSource:
let
filter_from_list = root: files:
let
all_paren_dirs = p:
if p == "." || p == "/"
then []
else [ p ] ++ all_paren_dirs (dirOf p);
whitelist_set = listToAttrs (
concatMap (p:
let full_path = toString (root + "/${p}"); in
map (p': { name = p'; value = true; }) (all_paren_dirs full_path)
) files
);
in
p: t: hasAttr (toString p) whitelist_set && extraFilter p t;
has_prefix = prefix: s:
prefix == builtins.substring 0 (builtins.stringLength prefix) s;
remove_prefix = prefix: s:
builtins.substring
(builtins.stringLength prefix)
(builtins.stringLength s - builtins.stringLength prefix)
s;
lines = s: filter (x : x != [] && x != "") (split "\n" s);
gitDatabase = gitRootDir + "/.git";
in
if builtins.pathExists gitDatabase
then
let
git_dir =
if builtins.pathExists (gitDatabase + "/index")
then gitDatabase
else # likely a git worktree, so follow the indirection
let
git_content = lines (readFile gitDatabase);
first_line = head git_content;
prefix = "gitdir: ";
ok = length git_content == 1 && has_prefix prefix first_line;
in
if ok
then /. + remove_prefix prefix first_line
else abort "gitSource.nix: Cannot parse ${toString gitDatabase}";
whitelist_file =
pkgs.runCommand "git-ls-files" {envVariable = true;} ''
cp ${git_dir + "/index"} index
echo "ref: refs/heads/master" > HEAD
mkdir objects refs
${pkgs.git}/bin/git --git-dir . ls-files | grep "${grepFor}" > $out
'';
whitelist = lines (readFile (whitelist_file.out));
filter = filter_from_list gitRootDir whitelist;
in
subdir: path {
name = baseNameOf (toString subdir);
path = if isString subdir then (gitRootDir + "/${subdir}") else subdir;
filter = filter;
}
else
trace "gitSource.nix: ${toString gitRootDir} does not seem to be a git repository,\nassuming it is a clean checkout." (
subdir: path {
name = baseNameOf (toString subdir);
path = if isString subdir then (gitRootDir + "/${subdir}") else subdir;
}
)