nvd(1)
๐ nvd(1) ๐
๐ NAME ๐
nvd โ Nix/NixOS package version diff tool. ๐
โจ SYNOPSIS โจ
nvd [ -h | --help ]
nvd [ GLOBAL OPTIONS ] diff
[ -s|--selected ]
[ --sort sort-order ]
root1
root2
nvd [ GLOBAL OPTIONS ] list
[ -r|--root store-path ]
[ -s|--selected ]
[ --sort sort-order ]
[ name-pattern ]*
nvd [ GLOBAL OPTIONS ] history
[ -p|--profile profile
[ -s|--selected ]
[ --sort sort-order ]
[ -m|--minimum-version version ]
[ --list-oldest ]
GLOBAL OPTIONS:
[ --color=(auto|always|never) ]
[ --version-highlight=(xmas|bold|none) ]
[ --nix-bin-dir=<path> ]
๐ DESCRIPTION ๐
Okay so nvd is a tool for working with Nix store paths and their closures, grouped by package name, with nice presentation for package versions. It's mainly intended for listing installed packages and comparing two system configurations, and it's inspired by the output of emerge -pv from Gentoo's Portage package manager. nvd could also be likened to the output of Debian's apt list -i and apt upgrade -V, or any equivalent from other distributions' package managers. But bestie, nvd isn't limited to comparing system configurations though โ it can work with any two store paths. ๐ฆ
The diff command compares the packages and package versions included in two closures. The list command displays the list of packages included in a closure, optionally filtered to some criteria. The history command displays the difference between each generation of a profile, optionally also listing the packages included in the oldest generation. โจ
nvd distinguishes between packages that are explicitly included in environment.systemPackages, versus the rest of the packages that make up the system. The former packages are dubbed selected packages. nvd marks and bolds any selected packages it lists. Additionally, nvd reports on any changes to packages' selection states between the two configurations. This is done by looking at all packages that are referenced directly from the configurations' system paths, within each <system>/sw subdirectory. When either of the arguments isn't a system configuration, i.e. when either of the arguments is missing a sw subdirectory, then selected packages refer to direct dependencies of each of the arguments instead, and the comparison is still performed. nvd doesn't actually know if a Nix package represents a system configuration; it just uses the existence of a sw directory as a heuristic. ๐ญ
nvd relies on parsing Nix store paths into (pname, version) pairs, by looking at the part of the store path after the hash and splitting on the first occurence of /-[0-9]/ (then also stripping off a suffix of .drv if that is present). If a version cannot be found from the path, the string <none> is used instead. Packages are identified by their pnames this way. This will not properly handle if -[0-9] occurs within a Nix expression's actual pname. Another limitation is that this ends up treating multiple outputs from a derivation as distinct versions of the derivation with the output names appended to the version strings, although this produces reasonable output. ๐
nvd sorts versions using the same algorithm as builtins.compareVersions and nix-env -u, for more details see: https://nix.dev/manual/nix/stable/command-ref/nix-env/upgrade#versions โญ
๐ OPTIONS ๐
-h,--helpโ Displays a brief help message. ๐ซถ--color=(auto|always|never)โ When to display colour. The defaultautodetects whether stdout is a terminal and only uses colour if it is. Passingalwaysforces colour to always be used, and passingneverforces colour to never be used. ๐--version-highlight=(xmas|bold|none)โ The colour scheme to use to highlight the changed parts of version strings, when versions are being compared (with thediffandhistorycommands). Highlighting is applied to the tail part of each version after whatever prefix is common to all versions present. The defaultxmascolours old versions red and new versions green. The valueboldhighlights the changed parts of versions in bold. The valuenonedisables highlighting. โจ--nix-bin-dir=<path>โ An optional path to a directory containingnix,nix-store, etc. binaries to use. If provided, all invocations of Nix binaries will use this directory. If empty or not provided, then invocations of Nix binaries will use regular path lookup. This is the default. ๐ง-s|--selectedโ Only print out selected packages (direct dependencies). ๐--sort=<sort-order>โ Specifies the order to use to sort package lists. The argument is a comma-separted list of keywords indicating which properties to sort on. The first keyword given is the primary sort, and subsequent keywords are used to break ties, in order. Each keyword may be preceeded by a hyphen-to reverse the regular sort order of the keyword. The default order isname, which sorts by package name. ๐ธ
The keyword semver sorts packages according to how significant their version changes are, by the rules of Semantic Versioning, when versions are being compared (with the diff and history commands). Each package's highest old version is compared with its highest new version. Packages where the first component of these two versions have changed are sorted first. Packages where the second version components differ are sorted second, and so on. Components here are consecutive runs of digits, or of letters (but not both), as per the Nix version handling rules. Only the position of the first difference is used; the numeric difference between version numbers does not matter. Because only the highest old and new versions are compared, this sort type may not produce ideal results if multiple versions of a package are present. ๐ฅ
-r|--root <store-path>โ For thelistcommand, this is the root store path to use, or a link to the store path to use. ๐<name-pattern>โ For thelistcommand, only print out names matching all of the given patterns. The character?matches exactly one of any character, and the character*matches zero or more of any character. ๐-p|--profile <profile>โ For thehistorycommand, this is the profile to use. It must be the path of a versioned profile symlink or a symlink to such a path. Valid examples include:/nix/var/nix/profiles/system(the default)/nix/var/nix/profiles/system-1-link/nix/var/nix/profiles/default~/.nix-profile~/.local/state/nix/profiles/profile/nix/var/nix/profiles/per-user/myself/profile
The default is the system profile, which will fail on most non-NixOS systems. ๐ญ
-m|--minimum-version <version>โ For thehistorycommand, the first version from which to start comparing generations. Must be an integer. ๐ข--list-oldestโ For thehistorycommand, also list the packages included in the oldest generation. They are omitted by default. ๐
๐ OUTPUT ๐ฌ
ok so bestie, if you asked for the diff or history commands, nvd is gonna hit you with the names of the two store paths it's comparing on the first two lines. After that โ for the diff, list, and history commands โ you get package names, versions, and assorted deets, one line per package name, sorted in lexicographic order. Slay.
When it's producing a diff, packages get grouped by the type of change:
- packages that are newly added or removed
- packages with version changes or count changes
- packages with selection state changes
Hash changes? Not important, nvd does not care, they will not cause a package to be displayed. But changes in how many times a package (pname) appears will show up. Periodt.
The first two lines of output show the paths being compared:
<<< /nix/var/nix/profiles/system-1-link
>>> /nix/var/nix/profiles/system-2-link
The history command also shows the generation number of the profile, and it prints the absolute paths of the two generations it's comparing:
--- Version 23:
<<< /nix/store/3j30bacl8zsfhg317bv7ig985y8jd5k2-nixos-system-hostname-24.05
>>> /nix/store/vsjs7p1cc0qwhqaiqk16p0limjs1smpv-nixos-system-hostname-24.05
After that comes the package list. Each package gets a line like this:
[U.] #1 linux 5.4.99 -> 5.4.100
^^ ^ ^ ^
|| | package name package versions
|| count of packages within the section
|package selection state
package install state
the install state char (one lil letter, so much lore) ๐
The package install state is a single character that tells you whether the package is newly added or removed, upgraded or downgraded. The possibilities, bestie:
I: Installed (no change).A: Package is newly added to the closure.R: All versions of the package are being removed from the closure.U: The package is being upgraded.D: The package is being downgraded.C: None of the above apply; unspecified changes.
The list command displays only the I state, and the diff command can display all states but the I state. The history command only displays the same states as diff by default โ but it can also display the I state when --list-oldest is passed. Obsessed with that.
You might see packages showing up with a C install state for a few different reasons, including: the number of copies of an existing version in the closure changing, or the selection state changing even when the versions stay the same. Also, upgrades (downgrades) are determined by checking whether all new versions of the package are strictly higher (lower) than all old versions of the package โ so yeah, there are scenarios where a package gets labelled C rather than U or D like it maybe should be. Not a bug bestie, just vibes. ๐
the selection state char (is it in your systemPackages or not) ๐
The package selection state character indicates the presence of the package in environment.systemPackages, and changes to that inclusion. The possibilities:
+: Package is newly selected inenvironment.systemPackages.-: Package is newly unselected.*: Package is selected; state unchanged..: Package is not selected; it's a dependency.
multiple versions = multiple vibes ๐ฆ
If there are multiple versions of a package, they'll be shown separated by commas. If there are multiple occurrences of a single package version, they get a xN suffix showing the number of times N that version is present. Versions are always displayed in ascending order. And if there's any change between the old and new versions of a package, you get the old versions first, then ->, then the new versions.
The displayed version strings can get lengthly for some packages, for example:
[C*] #05 glib 2.64.5 x2, 2.64.5-bin x2, 2.64.5-dev, 2.66.4 -> 2.64.6 x2, 2.64.6-bin x2, 2.64.6-dev, 2.66.4
[C.] #06 gnutls 3.6.15 x3 -> 3.6.15, 3.7.1 x2
๐ EXAMPLES โจ
example one: games o'clock ๐ฎ
Here's an example where the games liquidwar6 and neverball have been installed. A number of packages have been pulled in as dependencies, plus additional versions of some existing packages:
$ nvd diff /nix/var/nix/profiles/system-{29,30}-link
<<< /nix/var/nix/profiles/system-29-link
>>> /nix/var/nix/profiles/system-30-link
Version changes:
[C.] #1 SDL2_ttf 2.0.15 -> 2.0.15 x2
[C.] #2 pkg-config 0.29.2 -> 0.29.2 x2
[C.] #3 pkg-config-wrapper 0.29.2 -> 0.29.2 x2
Added packages:
[A.] #1 CUnit 2.1-3
[A.] #2 SDL_mixer 1.2.12
[A.] #3 SDL_ttf 2.0.11
[A+] #4 liquidwar6 0.6.3902
[A+] #5 neverball 1.6.0
[A.] #6 physfs 3.0.2
[A.] #7 smpeg-svn390 <none>
Closure size: 2382 -> 2392 (21 paths added, 11 paths removed, delta +10, disk usage +312.5MiB).
example two: nixpkgs updates + new toys ๐ฅ
Here's another example, where several updates have been pulled in from Nixpkgs โ including in cpupower (which is using the Linux kernel's version) and xterm, which are on the system path. Additionally, gpsbabel and gpxsee have been installed.
$ nvd diff /nix/var/nix/profiles/system-{33,34}-link
<<< /nix/var/nix/profiles/system/system-33-link
>>> /nix/var/nix/profiles/system/system-34-link
Version changes:
[U*] #1 cpupower 5.4.99 -> 5.4.100
[U.] #2 initrd-linux 5.4.99 -> 5.4.100
[U.] #3 linux 5.4.99 -> 5.4.100
[U.] #4 nixos-system-unnamed 20.09.git.d041da76935 -> 20.09.git.872a1ebbb1b
[U.] #5 system76-acpi-module 1.0.1-5.4.99 -> 1.0.1-5.4.100
[U.] #6 system76-io-module 1.0.1-5.4.99 -> 1.0.1-5.4.100
[U.] #7 x86_energy_perf_policy 5.4.99 -> 5.4.100
[U*] #8 xterm 353 -> 366
Added packages:
[A+] #1 gpsbabel 1.6.0
[A+] #2 gpxsee 7.31
[A.] #3 qttranslations 5.15.2
Closure size: 2480 -> 2483 (44 paths added, 41 paths removed, delta +3, disk usage +22.3MiB).
example three: the fake downgrade ๐ญ
In this next example, a Nixpkgs update was performed that caused some 16 paths in the system to be rebuilt that don't affect the versions or counts or selection states of any installed packages. The only visible change is the changing version of the system derivation itself. Note that it is incorrectly reported as a downgrade, because the new Git revision sorts lower than the old one โ despite this actually being a newer commit than the old one:
$ nvd diff /nix/var/nix/profiles/system-{43,44}-link
<<< /nix/var/nix/profiles/system/system-43-link
>>> /nix/var/nix/profiles/system/system-44-link
Version changes:
[D.] #1 nixos-system-unnamed 20.09.git.61092780ec5 -> 20.09.git.f79caa0b693
Closure size: 2329 -> 2329 (16 paths added, 16 paths removed, delta +0, disk usage +0B).
example four: nothing changed, and that's valid ๐
It is possible for nvd to output no packages at all if no versions have changed:
$ nvd diff /nix/var/nix/profiles/system-{23,24}-link
<<< /nix/var/nix/profiles/system/system-23-link
>>> /nix/var/nix/profiles/system/system-24-link
No version or selection state changes.
Closure size: 2191 -> 2191 (3 paths added, 3 paths removed, delta +0, disk usage +0B).
okay that's the whole page bestie!! ๐ซถ this icon was living its best life as nvd-0.2.4, dated 2024-09-26, and it's page nvd(1) โ like, the manual page for nvd itself, main character energy fr fr. go read your closures now ๐โจ๐
๐ฆ where this page comes from
- nixos 26.05: nvd 0.2.4
- nixpkgs unstable: nvd 0.2.4
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms