forked from lix-project/lix
Add command 'nix profile diff-closure'
This shows all changes between generations of a profile. E.g. $ nix profile diff-closures --profile /nix/var/nix/profiles/system Generation 654 -> 655: nix: 2.4pre20200617_5d69bbf → 2.4pre20200701_6ff9aa8, +42.2 KiB Generation 655 -> 656: blender-bin: 2.83.0 → 2.83.1, -294.2 KiB Generation 656 -> 657: curl: 7.68.0 → 7.70.0, +19.1 KiB firmware-linux-nonfree: 2020-01-22 → 2020-05-19, +30827.7 KiB ibus: -21.8 KiB initrd-linux: 5.4.46 → 5.4.49 ...
This commit is contained in:
parent
5517eee17e
commit
16c9f6762d
3 changed files with 104 additions and 45 deletions
|
@ -244,4 +244,10 @@ void completeFlakeRefWithFragment(
|
||||||
const Strings & defaultFlakeAttrPaths,
|
const Strings & defaultFlakeAttrPaths,
|
||||||
std::string_view prefix);
|
std::string_view prefix);
|
||||||
|
|
||||||
|
void printClosureDiff(
|
||||||
|
ref<Store> store,
|
||||||
|
const StorePath & beforePath,
|
||||||
|
const StorePath & afterPath,
|
||||||
|
std::string_view indent);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
using namespace nix;
|
namespace nix {
|
||||||
|
|
||||||
struct Info
|
struct Info
|
||||||
{
|
{
|
||||||
|
@ -52,6 +52,60 @@ std::string showVersions(const std::set<std::string> & versions)
|
||||||
return concatStringsSep(", ", versions2);
|
return concatStringsSep(", ", versions2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void printClosureDiff(
|
||||||
|
ref<Store> store,
|
||||||
|
const StorePath & beforePath,
|
||||||
|
const StorePath & afterPath,
|
||||||
|
std::string_view indent)
|
||||||
|
{
|
||||||
|
auto beforeClosure = getClosureInfo(store, beforePath);
|
||||||
|
auto afterClosure = getClosureInfo(store, afterPath);
|
||||||
|
|
||||||
|
std::set<std::string> allNames;
|
||||||
|
for (auto & [name, _] : beforeClosure) allNames.insert(name);
|
||||||
|
for (auto & [name, _] : afterClosure) allNames.insert(name);
|
||||||
|
|
||||||
|
for (auto & name : allNames) {
|
||||||
|
auto & beforeVersions = beforeClosure[name];
|
||||||
|
auto & afterVersions = afterClosure[name];
|
||||||
|
|
||||||
|
auto totalSize = [&](const std::map<std::string, std::map<StorePath, Info>> & versions)
|
||||||
|
{
|
||||||
|
uint64_t sum = 0;
|
||||||
|
for (auto & [_, paths] : versions)
|
||||||
|
for (auto & [path, _] : paths)
|
||||||
|
sum += store->queryPathInfo(path)->narSize;
|
||||||
|
return sum;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto beforeSize = totalSize(beforeVersions);
|
||||||
|
auto afterSize = totalSize(afterVersions);
|
||||||
|
auto sizeDelta = (int64_t) afterSize - (int64_t) beforeSize;
|
||||||
|
auto showDelta = abs(sizeDelta) >= 8 * 1024;
|
||||||
|
|
||||||
|
std::set<std::string> removed, unchanged;
|
||||||
|
for (auto & [version, _] : beforeVersions)
|
||||||
|
if (!afterVersions.count(version)) removed.insert(version); else unchanged.insert(version);
|
||||||
|
|
||||||
|
std::set<std::string> added;
|
||||||
|
for (auto & [version, _] : afterVersions)
|
||||||
|
if (!beforeVersions.count(version)) added.insert(version);
|
||||||
|
|
||||||
|
if (showDelta || !removed.empty() || !added.empty()) {
|
||||||
|
std::vector<std::string> items;
|
||||||
|
if (!removed.empty() || !added.empty())
|
||||||
|
items.push_back(fmt("%s → %s", showVersions(removed), showVersions(added)));
|
||||||
|
if (showDelta)
|
||||||
|
items.push_back(fmt("%s%+.1f KiB" ANSI_NORMAL, sizeDelta > 0 ? ANSI_RED : ANSI_GREEN, sizeDelta / 1024.0));
|
||||||
|
std::cout << fmt("%s%s: %s\n", indent, name, concatStringsSep(", ", items));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace nix;
|
||||||
|
|
||||||
struct CmdDiffClosures : SourceExprCommand
|
struct CmdDiffClosures : SourceExprCommand
|
||||||
{
|
{
|
||||||
std::string _before, _after;
|
std::string _before, _after;
|
||||||
|
@ -85,49 +139,7 @@ struct CmdDiffClosures : SourceExprCommand
|
||||||
auto beforePath = toStorePath(store, Realise::Outputs, operateOn, before);
|
auto beforePath = toStorePath(store, Realise::Outputs, operateOn, before);
|
||||||
auto after = parseInstallable(store, _after);
|
auto after = parseInstallable(store, _after);
|
||||||
auto afterPath = toStorePath(store, Realise::Outputs, operateOn, after);
|
auto afterPath = toStorePath(store, Realise::Outputs, operateOn, after);
|
||||||
|
printClosureDiff(store, beforePath, afterPath, "");
|
||||||
auto beforeClosure = getClosureInfo(store, beforePath);
|
|
||||||
auto afterClosure = getClosureInfo(store, afterPath);
|
|
||||||
|
|
||||||
std::set<std::string> allNames;
|
|
||||||
for (auto & [name, _] : beforeClosure) allNames.insert(name);
|
|
||||||
for (auto & [name, _] : afterClosure) allNames.insert(name);
|
|
||||||
|
|
||||||
for (auto & name : allNames) {
|
|
||||||
auto & beforeVersions = beforeClosure[name];
|
|
||||||
auto & afterVersions = afterClosure[name];
|
|
||||||
|
|
||||||
auto totalSize = [&](const std::map<std::string, std::map<StorePath, Info>> & versions)
|
|
||||||
{
|
|
||||||
uint64_t sum = 0;
|
|
||||||
for (auto & [_, paths] : versions)
|
|
||||||
for (auto & [path, _] : paths)
|
|
||||||
sum += store->queryPathInfo(path)->narSize;
|
|
||||||
return sum;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto beforeSize = totalSize(beforeVersions);
|
|
||||||
auto afterSize = totalSize(afterVersions);
|
|
||||||
auto sizeDelta = (int64_t) afterSize - (int64_t) beforeSize;
|
|
||||||
auto showDelta = abs(sizeDelta) >= 8 * 1024;
|
|
||||||
|
|
||||||
std::set<std::string> removed, unchanged;
|
|
||||||
for (auto & [version, _] : beforeVersions)
|
|
||||||
if (!afterVersions.count(version)) removed.insert(version); else unchanged.insert(version);
|
|
||||||
|
|
||||||
std::set<std::string> added;
|
|
||||||
for (auto & [version, _] : afterVersions)
|
|
||||||
if (!beforeVersions.count(version)) added.insert(version);
|
|
||||||
|
|
||||||
if (showDelta || !removed.empty() || !added.empty()) {
|
|
||||||
std::vector<std::string> items;
|
|
||||||
if (!removed.empty() || !added.empty())
|
|
||||||
items.push_back(fmt("%s → %s", showVersions(removed), showVersions(added)));
|
|
||||||
if (showDelta)
|
|
||||||
items.push_back(fmt("%s%+.1f KiB" ANSI_NORMAL, sizeDelta > 0 ? ANSI_RED : ANSI_GREEN, sizeDelta / 1024.0));
|
|
||||||
std::cout << fmt("%s: %s\n", name, concatStringsSep(", ", items));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "builtins/buildenv.hh"
|
#include "builtins/buildenv.hh"
|
||||||
#include "flake/flakeref.hh"
|
#include "flake/flakeref.hh"
|
||||||
#include "../nix-env/user-env.hh"
|
#include "../nix-env/user-env.hh"
|
||||||
|
#include "profiles.hh"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
@ -394,6 +395,46 @@ struct CmdProfileInfo : virtual EvalCommand, virtual StoreCommand, MixDefaultPro
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CmdProfileDiffClosures : virtual EvalCommand, virtual StoreCommand, MixDefaultProfile
|
||||||
|
{
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "show the closure difference between each generation of a profile";
|
||||||
|
}
|
||||||
|
|
||||||
|
Examples examples() override
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
Example{
|
||||||
|
"To show what changed between each generation of the NixOS system profile:",
|
||||||
|
"nix profile diff-closure --profile /nix/var/nix/profiles/system"
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(ref<Store> store) override
|
||||||
|
{
|
||||||
|
auto [gens, curGen] = findGenerations(*profile);
|
||||||
|
|
||||||
|
std::optional<Generation> prevGen;
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
for (auto & gen : gens) {
|
||||||
|
if (prevGen) {
|
||||||
|
if (!first) std::cout << "\n";
|
||||||
|
first = false;
|
||||||
|
std::cout << fmt("Generation %d -> %d:\n", prevGen->number, gen.number);
|
||||||
|
printClosureDiff(store,
|
||||||
|
store->followLinksToStorePath(prevGen->path),
|
||||||
|
store->followLinksToStorePath(gen.path),
|
||||||
|
" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
prevGen = gen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct CmdProfile : virtual MultiCommand, virtual Command
|
struct CmdProfile : virtual MultiCommand, virtual Command
|
||||||
{
|
{
|
||||||
CmdProfile()
|
CmdProfile()
|
||||||
|
@ -402,6 +443,7 @@ struct CmdProfile : virtual MultiCommand, virtual Command
|
||||||
{"remove", []() { return make_ref<CmdProfileRemove>(); }},
|
{"remove", []() { return make_ref<CmdProfileRemove>(); }},
|
||||||
{"upgrade", []() { return make_ref<CmdProfileUpgrade>(); }},
|
{"upgrade", []() { return make_ref<CmdProfileUpgrade>(); }},
|
||||||
{"info", []() { return make_ref<CmdProfileInfo>(); }},
|
{"info", []() { return make_ref<CmdProfileInfo>(); }},
|
||||||
|
{"diff-closures", []() { return make_ref<CmdProfileDiffClosures>(); }},
|
||||||
})
|
})
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
@ -425,4 +467,3 @@ struct CmdProfile : virtual MultiCommand, virtual Command
|
||||||
};
|
};
|
||||||
|
|
||||||
static auto r1 = registerCommand<CmdProfile>("profile");
|
static auto r1 = registerCommand<CmdProfile>("profile");
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue