* Revived the old "nix-store --delete" operation that deletes the
specified paths from the Nix store. However, this operation is safe: it refuses to delete anything that the garbage collector wouldn't delete.
This commit is contained in:
parent
3c5619c7e4
commit
4b9e7f59ca
4 changed files with 45 additions and 11 deletions
|
@ -303,8 +303,8 @@ static Paths topoSort(const PathSet & paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void collectGarbage(GCAction action, PathSet & result,
|
void collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||||
unsigned long long & bytesFreed)
|
PathSet & result, unsigned long long & bytesFreed)
|
||||||
{
|
{
|
||||||
result.clear();
|
result.clear();
|
||||||
bytesFreed = 0;
|
bytesFreed = 0;
|
||||||
|
@ -398,17 +398,26 @@ void collectGarbage(GCAction action, PathSet & result,
|
||||||
|
|
||||||
/* Read the Nix store directory to find all currently existing
|
/* Read the Nix store directory to find all currently existing
|
||||||
paths. */
|
paths. */
|
||||||
Paths storePaths = readDirectory(nixStore);
|
PathSet storePathSet;
|
||||||
PathSet storePaths2;
|
if (action != gcDeleteSpecific) {
|
||||||
for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i)
|
Paths entries = readDirectory(nixStore);
|
||||||
storePaths2.insert(canonPath(nixStore + "/" + *i));
|
for (Paths::iterator i = entries.begin(); i != entries.end(); ++i)
|
||||||
|
storePathSet.insert(canonPath(nixStore + "/" + *i));
|
||||||
|
} else {
|
||||||
|
for (PathSet::iterator i = pathsToDelete.begin();
|
||||||
|
i != pathsToDelete.end(); ++i)
|
||||||
|
{
|
||||||
|
assertStorePath(*i);
|
||||||
|
storePathSet.insert(*i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Topologically sort them under the `referrers' relation. That
|
/* Topologically sort them under the `referrers' relation. That
|
||||||
is, a < b iff a is in referrers(b). This gives us the order in
|
is, a < b iff a is in referrers(b). This gives us the order in
|
||||||
which things can be deleted safely. */
|
which things can be deleted safely. */
|
||||||
/* !!! when we have multiple output paths per derivation, this
|
/* !!! when we have multiple output paths per derivation, this
|
||||||
will not work anymore because we get cycles. */
|
will not work anymore because we get cycles. */
|
||||||
storePaths = topoSort(storePaths2);
|
Paths storePaths = topoSort(storePathSet);
|
||||||
|
|
||||||
/* Try to delete store paths in the topologically sorted order. */
|
/* Try to delete store paths in the topologically sorted order. */
|
||||||
for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) {
|
for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) {
|
||||||
|
@ -416,6 +425,8 @@ void collectGarbage(GCAction action, PathSet & result,
|
||||||
debug(format("considering deletion of `%1%'") % *i);
|
debug(format("considering deletion of `%1%'") % *i);
|
||||||
|
|
||||||
if (livePaths.find(*i) != livePaths.end()) {
|
if (livePaths.find(*i) != livePaths.end()) {
|
||||||
|
if (action == gcDeleteSpecific)
|
||||||
|
throw Error(format("cannot delete path `%1%' since it is still alive") % *i);
|
||||||
debug(format("live path `%1%'") % *i);
|
debug(format("live path `%1%'") % *i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -430,7 +441,7 @@ void collectGarbage(GCAction action, PathSet & result,
|
||||||
|
|
||||||
AutoCloseFD fdLock;
|
AutoCloseFD fdLock;
|
||||||
|
|
||||||
if (action == gcDeleteDead) {
|
if (action == gcDeleteDead || action == gcDeleteSpecific) {
|
||||||
|
|
||||||
/* Only delete a lock file if we can acquire a write lock
|
/* Only delete a lock file if we can acquire a write lock
|
||||||
on it. That means that it's either stale, or the
|
on it. That means that it's either stale, or the
|
||||||
|
|
|
@ -10,6 +10,7 @@ typedef enum {
|
||||||
gcReturnLive,
|
gcReturnLive,
|
||||||
gcReturnDead,
|
gcReturnDead,
|
||||||
gcDeleteDead,
|
gcDeleteDead,
|
||||||
|
gcDeleteSpecific,
|
||||||
} GCAction;
|
} GCAction;
|
||||||
|
|
||||||
/* If `action' is set to `gcReturnRoots', find and return the set of
|
/* If `action' is set to `gcReturnRoots', find and return the set of
|
||||||
|
@ -19,8 +20,8 @@ typedef enum {
|
||||||
closure of) the roots. If `action' is `gcReturnDead', return the
|
closure of) the roots. If `action' is `gcReturnDead', return the
|
||||||
set of paths not reachable from the roots. If `action' is
|
set of paths not reachable from the roots. If `action' is
|
||||||
`gcDeleteDead', actually delete the latter set. */
|
`gcDeleteDead', actually delete the latter set. */
|
||||||
void collectGarbage(GCAction action, PathSet & result,
|
void collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||||
unsigned long long & bytesFreed);
|
PathSet & result, unsigned long long & bytesFreed);
|
||||||
|
|
||||||
/* Register a temporary GC root. This root will automatically
|
/* Register a temporary GC root. This root will automatically
|
||||||
disappear when this process exits. WARNING: this function should
|
disappear when this process exits. WARNING: this function should
|
||||||
|
|
|
@ -7,6 +7,7 @@ Operations:
|
||||||
--realise / -r: ensure path validity; if a derivation, ensure that
|
--realise / -r: ensure path validity; if a derivation, ensure that
|
||||||
validity of the outputs
|
validity of the outputs
|
||||||
--add / -A: copy a path to the Nix store
|
--add / -A: copy a path to the Nix store
|
||||||
|
--delete: safely delete paths from the Nix store
|
||||||
--query / -q: query information
|
--query / -q: query information
|
||||||
|
|
||||||
--register-substitutes: register a substitute expression (dangerous!)
|
--register-substitutes: register a substitute expression (dangerous!)
|
||||||
|
|
|
@ -518,7 +518,7 @@ static void opGC(Strings opFlags, Strings opArgs)
|
||||||
|
|
||||||
PathSet result;
|
PathSet result;
|
||||||
PrintFreed freed(action == gcDeleteDead);
|
PrintFreed freed(action == gcDeleteDead);
|
||||||
collectGarbage(action, result, freed.bytesFreed);
|
collectGarbage(action, PathSet(), result, freed.bytesFreed);
|
||||||
|
|
||||||
if (action != gcDeleteDead) {
|
if (action != gcDeleteDead) {
|
||||||
for (PathSet::iterator i = result.begin(); i != result.end(); ++i)
|
for (PathSet::iterator i = result.begin(); i != result.end(); ++i)
|
||||||
|
@ -527,6 +527,25 @@ static void opGC(Strings opFlags, Strings opArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Remove paths from the Nix store if possible (i.e., if they do not
|
||||||
|
have any remaining referrers and are not reachable from any GC
|
||||||
|
roots). */
|
||||||
|
static void opDelete(Strings opFlags, Strings opArgs)
|
||||||
|
{
|
||||||
|
if (!opFlags.empty()) throw UsageError("unknown flag");
|
||||||
|
|
||||||
|
PathSet pathsToDelete;
|
||||||
|
for (Strings::iterator i = opArgs.begin();
|
||||||
|
i != opArgs.end(); ++i)
|
||||||
|
pathsToDelete.insert(fixPath(*i));
|
||||||
|
|
||||||
|
PathSet dummy;
|
||||||
|
PrintFreed freed(true);
|
||||||
|
collectGarbage(gcDeleteSpecific, pathsToDelete,
|
||||||
|
dummy, freed.bytesFreed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* A sink that writes dump output to stdout. */
|
/* A sink that writes dump output to stdout. */
|
||||||
struct StdoutSink : DumpSink
|
struct StdoutSink : DumpSink
|
||||||
{
|
{
|
||||||
|
@ -621,6 +640,8 @@ void run(Strings args)
|
||||||
op = opAddFixed;
|
op = opAddFixed;
|
||||||
else if (arg == "--print-fixed-path")
|
else if (arg == "--print-fixed-path")
|
||||||
op = opPrintFixedPath;
|
op = opPrintFixedPath;
|
||||||
|
else if (arg == "--delete")
|
||||||
|
op = opDelete;
|
||||||
else if (arg == "--query" || arg == "-q")
|
else if (arg == "--query" || arg == "-q")
|
||||||
op = opQuery;
|
op = opQuery;
|
||||||
else if (arg == "--register-substitutes")
|
else if (arg == "--register-substitutes")
|
||||||
|
|
Loading…
Reference in a new issue