1 #!/usr/bin/env nix-shell
2 #! nix-shell -i bash -p coreutils gnugrep gnused
4 ################################################################################
6 ################################################################################
7 # This script "diffs" Nix profile generations. #
10 ################################################################################
11 # > nix-diff.sh 90 92 #
13 # + gnumake-4.2.1-doc #
15 ################################################################################
16 # The example shows that as of generation 92 and since generation 90, #
17 # gnumake-4.2.1 and gnumake-4.2.1-doc have been installed, while #
18 # htmldoc-1.8.29 has been removed. #
20 # The example above shows the default, minimal output mode of this script. #
21 # For more features, run `nix-diff.sh -h` for usage instructions. #
22 ################################################################################
26 usage: nix-diff.sh [-h | [-p profile | -s] [-q] [-l] [range]]
27 -h: print this message before exiting
28 -q: list the derivations installed in the parent generation
29 -l: diff every available intermediate generation between parent and
31 -p profile: specify the Nix profile to use
32 * defaults to ~/.nix-profile
33 -s: use the system profile
34 * equivalent to: -p /nix/var/nix/profiles/system
35 profile: * should be something like /nix/var/nix/profiles/default, not a
36 generation link like /nix/var/nix/profiles/default-2-link
37 range: the range of generations to diff
38 * the following patterns are allowed, where A, B, and N are positive
39 integers, and G is the currently active generation:
40 A..B => diffs from generation A to generation B
41 ~N => diffs from the Nth newest generation (older than G) to G
42 A => diffs from generation A to G
48 echo 'run `nix-diff.sh -h` for usage instructions' >&2
52 while getopts :hqlp
:s opt
; do
68 opt_profile
=/nix
/var
/nix
/profiles
/system
71 echo "error: invalid option -$OPTARG" >&2
78 if [ -n "$opt_profile" ]; then
79 if ! [ -L "$opt_profile" ]; then
80 echo "error: expecting \`$opt_profile\` to be a symbolic link" >&2
84 opt_profile
=$
(readlink ~
/.nix-profile
)
85 if (( $?
!= 0 )); then
86 echo 'error: unable to dereference `~/.nix-profile`' >&2
87 echo 'specify the profile manually with the `-p` flag' >&2
93 nix-env
-p "$opt_profile" --list-generations \
99 nix-env
-p "$opt_profile" --list-generations \
100 |
grep -E '\(current\)\s*$' \
101 |
sed -r 's:^\s*::' \
106 local i
=0 from
=$1 n
=$2 tmp
107 for gen
in $
(list_gens |
sort -rn); do
108 if ((gen
< from
)); then
115 echo -n "error: there aren't $n generation(s) older than" >&2
116 echo " generation $from" >&2
124 for i
in $
(seq $
(($#-1))); do
125 if grep -E "^${argv[$i]}\$" <(echo "$1") >/dev
/null
; then
133 case $
(match
"$1" '' '[0-9]+' '[0-9]+\.\.[0-9]+' '~[0-9]+') in
135 diffTo
=$
(current_gen
)
136 diffFrom
=$
(neg_gen
$diffTo 1)
137 (($?
== 1)) && usage_tip
141 diffTo
=$
(current_gen
)
148 diffTo
=$
(current_gen
)
149 diffFrom
=$
(neg_gen
$diffTo ${1#*~})
150 (($?
== 1)) && usage_tip
153 echo 'error: invalid invocation' >&2
158 dirA
="${opt_profile}-${diffFrom}-link"
159 dirB
="${opt_profile}-${diffTo}-link"
161 declare -a temp_files
163 echo -n ${#temp_files[@]}
166 temp_files
[$
(temp_length
)]=$
(mktemp
)
169 rm -f ${temp_files[@]}
172 echo -n "${temp_files[$(($(temp_length)-1))]}"
174 trap 'temp_clean' EXIT
178 refs
=$
(nix-store
-q --references "$dirA")
179 (( $?
!= 0 )) && exit 1
181 |
grep -v env-manifest.nix \
187 nix-env
-p "$opt_profile" --list-generations \
188 |
grep -E "^\s*${gen}" \
189 |
sed -r 's:^\s*::' \
193 if [ -n "$opt_query" ]; then
196 |
sed -r 's:^[^-]+-(.*)$: \1:'
201 if [ -n "$opt_log" ]; then
202 gens
=$
(for gen
in $
(list_gens
); do
203 ((diffFrom
< gen
&& gen
< diffTo
)) && echo $gen
205 # Force the $diffTo generation to be included in this list, instead of using
206 # `gen <= diffTo` in the preceding loop, so we encounter an error upon the
207 # event of its nonexistence.
223 [ -n "$print_line" ] && echo
228 dirB
="${opt_profile}-${gen}-link"
229 refs
=$
(nix-store
-q --references "$dirB")
230 (( $?
!= 0 )) && exit 1
232 |
grep -v env-manifest.nix \
236 in=$
(comm -3 -1 "$versA" "$versB")
237 sed -r 's:^[^-]*-(.*)$:\1+:' <(echo "$in") \
241 un
=$
(comm -3 -2 "$versA" "$versB")
242 sed -r 's:^[^-]*-(.*)$:\1-:' <(echo "$un") \
248 |
sed -r 's:(.*)-$:- \1:' \
249 |
sed -r 's:(.*)\+$:\+ \1:' \
253 if [ -n "$opt_query" -o -n "$opt_log" ]; then
255 lines
=$
(wc -l "$out" | cut
-d' ' -f1)
256 tag
=$
(print_tag
"$gen")
257 (( $?
!= 0 )) && exit 1
258 if [ $lines -eq 0 ]; then
259 echo "$tag (no change)"
269 echo "diffing from generation $diffFrom to $diffTo"