forked from lix-project/lix
Add 'nix profile history' command
Replaces 'nix-env --list-generations'. Similar to 'nix profile diff-closures' but shows only the changes in top-level packages.
This commit is contained in:
parent
29007f8bc6
commit
2f463e90ed
|
@ -261,6 +261,8 @@ void completeFlakeRefWithFragment(
|
||||||
const Strings & defaultFlakeAttrPaths,
|
const Strings & defaultFlakeAttrPaths,
|
||||||
std::string_view prefix);
|
std::string_view prefix);
|
||||||
|
|
||||||
|
std::string showVersions(const std::set<std::string> & versions);
|
||||||
|
|
||||||
void printClosureDiff(
|
void printClosureDiff(
|
||||||
ref<Store> store,
|
ref<Store> store,
|
||||||
const StorePath & beforePath,
|
const StorePath & beforePath,
|
||||||
|
|
26
src/nix/profile-history.md
Normal file
26
src/nix/profile-history.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
R""(
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
* Show the changes between each version of your default profile:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix profile history
|
||||||
|
Version 508 -> 509:
|
||||||
|
flake:nixpkgs#legacyPackages.x86_64-linux.awscli: ∅ -> 1.17.13
|
||||||
|
|
||||||
|
Version 509 -> 510:
|
||||||
|
flake:nixpkgs#legacyPackages.x86_64-linux.awscli: 1.17.13 -> 1.18.211
|
||||||
|
```
|
||||||
|
|
||||||
|
# Description
|
||||||
|
|
||||||
|
This command shows what packages were added, removed or upgraded
|
||||||
|
between subsequent versions of a profile. It only shows top-level
|
||||||
|
packages, not dependencies; for that, use [`nix profile
|
||||||
|
diff-closures`](./nix3-profile-diff-closures.md).
|
||||||
|
|
||||||
|
The addition of a package to a profile is denoted by the string `∅ ->`
|
||||||
|
*version*, whereas the removal is denoted by *version* `-> ∅`.
|
||||||
|
|
||||||
|
)""
|
|
@ -8,6 +8,7 @@
|
||||||
#include "flake/flakeref.hh"
|
#include "flake/flakeref.hh"
|
||||||
#include "../nix-env/user-env.hh"
|
#include "../nix-env/user-env.hh"
|
||||||
#include "profiles.hh"
|
#include "profiles.hh"
|
||||||
|
#include "names.hh"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
@ -21,6 +22,13 @@ struct ProfileElementSource
|
||||||
FlakeRef resolvedRef;
|
FlakeRef resolvedRef;
|
||||||
std::string attrPath;
|
std::string attrPath;
|
||||||
// FIXME: output names
|
// FIXME: output names
|
||||||
|
|
||||||
|
bool operator < (const ProfileElementSource & other) const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
std::pair(originalRef.to_string(), attrPath) <
|
||||||
|
std::pair(other.originalRef.to_string(), other.attrPath);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ProfileElement
|
struct ProfileElement
|
||||||
|
@ -29,6 +37,29 @@ struct ProfileElement
|
||||||
std::optional<ProfileElementSource> source;
|
std::optional<ProfileElementSource> source;
|
||||||
bool active = true;
|
bool active = true;
|
||||||
// FIXME: priority
|
// FIXME: priority
|
||||||
|
|
||||||
|
std::string describe() const
|
||||||
|
{
|
||||||
|
if (source)
|
||||||
|
return fmt("%s#%s", source->originalRef, source->attrPath);
|
||||||
|
StringSet names;
|
||||||
|
for (auto & path : storePaths)
|
||||||
|
names.insert(DrvName(path.name()).name);
|
||||||
|
return concatStringsSep(", ", names);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string versions() const
|
||||||
|
{
|
||||||
|
StringSet versions;
|
||||||
|
for (auto & path : storePaths)
|
||||||
|
versions.insert(DrvName(path.name()).version);
|
||||||
|
return showVersions(versions);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator < (const ProfileElement & other) const
|
||||||
|
{
|
||||||
|
return std::tuple(describe(), storePaths) < std::tuple(other.describe(), other.storePaths);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ProfileManifest
|
struct ProfileManifest
|
||||||
|
@ -142,6 +173,46 @@ struct ProfileManifest
|
||||||
|
|
||||||
return std::move(info.path);
|
return std::move(info.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void printDiff(const ProfileManifest & prev, const ProfileManifest & cur, std::string_view indent)
|
||||||
|
{
|
||||||
|
auto prevElems = prev.elements;
|
||||||
|
std::sort(prevElems.begin(), prevElems.end());
|
||||||
|
|
||||||
|
auto curElems = cur.elements;
|
||||||
|
std::sort(curElems.begin(), curElems.end());
|
||||||
|
|
||||||
|
auto i = prevElems.begin();
|
||||||
|
auto j = curElems.begin();
|
||||||
|
|
||||||
|
bool changes = false;
|
||||||
|
|
||||||
|
while (i != prevElems.end() || j != curElems.end()) {
|
||||||
|
if (j != curElems.end() && (i == prevElems.end() || i->describe() > j->describe())) {
|
||||||
|
std::cout << fmt("%s%s: ∅ -> %s\n", indent, j->describe(), j->versions());
|
||||||
|
changes = true;
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
else if (i != prevElems.end() && (j == curElems.end() || i->describe() < j->describe())) {
|
||||||
|
std::cout << fmt("%s%s: %s -> ∅\n", indent, i->describe(), i->versions());
|
||||||
|
changes = true;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto v1 = i->versions();
|
||||||
|
auto v2 = j->versions();
|
||||||
|
if (v1 != v2) {
|
||||||
|
std::cout << fmt("%s%s: %s -> %s\n", indent, i->describe(), v1, v2);
|
||||||
|
changes = true;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changes)
|
||||||
|
std::cout << fmt("%sNo changes.\n", indent);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
|
struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
|
||||||
|
@ -401,6 +472,48 @@ struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CmdProfileHistory : virtual StoreCommand, EvalCommand, MixDefaultProfile
|
||||||
|
{
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "show all versions of a profile";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string doc() override
|
||||||
|
{
|
||||||
|
return
|
||||||
|
#include "profile-history.md"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(ref<Store> store) override
|
||||||
|
{
|
||||||
|
auto [gens, curGen] = findGenerations(*profile);
|
||||||
|
|
||||||
|
std::optional<std::pair<Generation, ProfileManifest>> prevGen;
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
for (auto & gen : gens) {
|
||||||
|
ProfileManifest manifest(*getEvalState(), gen.path);
|
||||||
|
|
||||||
|
if (!first) std::cout << "\n";
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
if (prevGen)
|
||||||
|
std::cout << fmt("Version %d -> %d:\n", prevGen->first.number, gen.number);
|
||||||
|
else
|
||||||
|
std::cout << fmt("Version %d:\n", gen.number);
|
||||||
|
|
||||||
|
ProfileManifest::printDiff(
|
||||||
|
prevGen ? prevGen->second : ProfileManifest(),
|
||||||
|
manifest,
|
||||||
|
" ");
|
||||||
|
|
||||||
|
prevGen = {gen, std::move(manifest)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct CmdProfile : NixMultiCommand
|
struct CmdProfile : NixMultiCommand
|
||||||
{
|
{
|
||||||
CmdProfile()
|
CmdProfile()
|
||||||
|
@ -410,6 +523,7 @@ struct CmdProfile : NixMultiCommand
|
||||||
{"upgrade", []() { return make_ref<CmdProfileUpgrade>(); }},
|
{"upgrade", []() { return make_ref<CmdProfileUpgrade>(); }},
|
||||||
{"list", []() { return make_ref<CmdProfileList>(); }},
|
{"list", []() { return make_ref<CmdProfileList>(); }},
|
||||||
{"diff-closures", []() { return make_ref<CmdProfileDiffClosures>(); }},
|
{"diff-closures", []() { return make_ref<CmdProfileDiffClosures>(); }},
|
||||||
|
{"history", []() { return make_ref<CmdProfileHistory>(); }},
|
||||||
})
|
})
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue