* 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,
|
||||
unsigned long long & bytesFreed)
|
||||
void collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
PathSet & result, unsigned long long & bytesFreed)
|
||||
{
|
||||
result.clear();
|
||||
bytesFreed = 0;
|
||||
|
@ -398,17 +398,26 @@ void collectGarbage(GCAction action, PathSet & result,
|
|||
|
||||
/* Read the Nix store directory to find all currently existing
|
||||
paths. */
|
||||
Paths storePaths = readDirectory(nixStore);
|
||||
PathSet storePaths2;
|
||||
for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i)
|
||||
storePaths2.insert(canonPath(nixStore + "/" + *i));
|
||||
PathSet storePathSet;
|
||||
if (action != gcDeleteSpecific) {
|
||||
Paths entries = readDirectory(nixStore);
|
||||
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
|
||||
is, a < b iff a is in referrers(b). This gives us the order in
|
||||
which things can be deleted safely. */
|
||||
/* !!! when we have multiple output paths per derivation, this
|
||||
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. */
|
||||
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);
|
||||
|
||||
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);
|
||||
continue;
|
||||
}
|
||||
|
@ -430,7 +441,7 @@ void collectGarbage(GCAction action, PathSet & result,
|
|||
|
||||
AutoCloseFD fdLock;
|
||||
|
||||
if (action == gcDeleteDead) {
|
||||
if (action == gcDeleteDead || action == gcDeleteSpecific) {
|
||||
|
||||
/* Only delete a lock file if we can acquire a write lock
|
||||
on it. That means that it's either stale, or the
|
||||
|
|
|
@ -10,6 +10,7 @@ typedef enum {
|
|||
gcReturnLive,
|
||||
gcReturnDead,
|
||||
gcDeleteDead,
|
||||
gcDeleteSpecific,
|
||||
} GCAction;
|
||||
|
||||
/* 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
|
||||
set of paths not reachable from the roots. If `action' is
|
||||
`gcDeleteDead', actually delete the latter set. */
|
||||
void collectGarbage(GCAction action, PathSet & result,
|
||||
unsigned long long & bytesFreed);
|
||||
void collectGarbage(GCAction action, const PathSet & pathsToDelete,
|
||||
PathSet & result, unsigned long long & bytesFreed);
|
||||
|
||||
/* Register a temporary GC root. This root will automatically
|
||||
disappear when this process exits. WARNING: this function should
|
||||
|
|
|
@ -7,6 +7,7 @@ Operations:
|
|||
--realise / -r: ensure path validity; if a derivation, ensure that
|
||||
validity of the outputs
|
||||
--add / -A: copy a path to the Nix store
|
||||
--delete: safely delete paths from the Nix store
|
||||
--query / -q: query information
|
||||
|
||||
--register-substitutes: register a substitute expression (dangerous!)
|
||||
|
|
|
@ -518,7 +518,7 @@ static void opGC(Strings opFlags, Strings opArgs)
|
|||
|
||||
PathSet result;
|
||||
PrintFreed freed(action == gcDeleteDead);
|
||||
collectGarbage(action, result, freed.bytesFreed);
|
||||
collectGarbage(action, PathSet(), result, freed.bytesFreed);
|
||||
|
||||
if (action != gcDeleteDead) {
|
||||
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. */
|
||||
struct StdoutSink : DumpSink
|
||||
{
|
||||
|
@ -621,6 +640,8 @@ void run(Strings args)
|
|||
op = opAddFixed;
|
||||
else if (arg == "--print-fixed-path")
|
||||
op = opPrintFixedPath;
|
||||
else if (arg == "--delete")
|
||||
op = opDelete;
|
||||
else if (arg == "--query" || arg == "-q")
|
||||
op = opQuery;
|
||||
else if (arg == "--register-substitutes")
|
||||
|
|
Loading…
Reference in a new issue