diff --git a/doc/manual/nix-collect-garbage.xml b/doc/manual/nix-collect-garbage.xml
index adc6c1e73..1de50408e 100644
--- a/doc/manual/nix-collect-garbage.xml
+++ b/doc/manual/nix-collect-garbage.xml
@@ -11,6 +11,7 @@
+ age
@@ -56,6 +57,16 @@
+
+ age
+
+
+ This option corresponds to the
+ option in nix-store .
+
+
+
+
diff --git a/doc/manual/nix-store.xml b/doc/manual/nix-store.xml
index febe02fd4..522de60d3 100644
--- a/doc/manual/nix-store.xml
+++ b/doc/manual/nix-store.xml
@@ -147,6 +147,7 @@
+ age
@@ -207,6 +208,17 @@
Each line should contain exactly one store path.
+
+ The option specifies a minimum time
+ in hours that an unreachable store path must not have been
+ used before it is considered dead. The default is 0 (consider
+ all unreachable store paths dead). Whether a store path has
+ been used is determined by looking at its access time
+ (atime), so this does not work if the store
+ is located on a file system that has the
+ noatime option set.
+
+
You generally will want to use the command
diff --git a/scripts/nix-collect-garbage.in b/scripts/nix-collect-garbage.in
index 539979cbb..44bcc16bb 100755
--- a/scripts/nix-collect-garbage.in
+++ b/scripts/nix-collect-garbage.in
@@ -9,16 +9,24 @@ my $storeDir = "@storedir@";
my %alive;
my $gcOper = "--delete";
-my $keepSuccessors = 1;
+my $minAge = 0;
my @roots = ();
# Parse the command line.
-foreach my $arg (@ARGV) {
+for (my $i = 0; $i < scalar @ARGV; $i++) {
+ my $arg = $ARGV[$i];
if ($arg eq "--delete" || $arg eq "--print-live" || $arg eq "--print-dead") {
$gcOper = $arg;
- } else { die "unknown argument `$arg'" };
+ }
+ elsif ($arg eq "--min-age") {
+ $i++;
+ $minAge = undef;
+ $minAge = $ARGV[$i];
+ die "invalid minimum age" unless defined $minAge && $minAge =~ /^\d*$/;
+ }
+ else { die "unknown argument `$arg'" };
}
@@ -69,7 +77,7 @@ findRoots 1, $rootsDir;
# Run the collector with the roots we found.
-my $pid = open2(">&1", \*WRITE, "@bindir@/nix-store --gc $gcOper")
+my $pid = open2(">&1", \*WRITE, "@bindir@/nix-store --gc $gcOper --min-age $minAge")
or die "cannot run `nix-store --gc'";
foreach my $root (@roots) {
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index aed7c2294..9af957693 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -2,6 +2,11 @@
#include "globals.hh"
+#include
+#include
+#include
+
+
void followLivePaths(Path nePath, PathSet & live)
{
/* Just to be sure, canonicalise the path. It is important to do
@@ -62,16 +67,26 @@ PathSet findLivePaths(const Paths & roots)
}
-PathSet findDeadPaths(const PathSet & live)
+PathSet findDeadPaths(const PathSet & live, time_t minAge)
{
PathSet dead;
startNest(nest, lvlDebug, "finding dead paths");
+ time_t now = time(0);
+
Strings storeNames = readDirectory(nixStore);
for (Strings::iterator i = storeNames.begin(); i != storeNames.end(); ++i) {
Path p = canonPath(nixStore + "/" + *i);
+
+ if (minAge > 0) {
+ struct stat st;
+ if (lstat(p.c_str(), &st) != 0)
+ throw SysError(format("obtaining information about `%1%'") % p);
+ if (st.st_atime + minAge >= now) continue;
+ }
+
if (live.find(p) == live.end()) {
debug(format("dead path `%1%'") % p);
dead.insert(p);
diff --git a/src/libstore/gc.hh b/src/libstore/gc.hh
index 997057ba9..1ada419da 100644
--- a/src/libstore/gc.hh
+++ b/src/libstore/gc.hh
@@ -17,8 +17,10 @@ PathSet findLivePaths(const Paths & roots);
/* Given a set of "live" store paths, determine the set of "dead"
store paths (which are simply all store paths that are not in the
- live set). */
-PathSet findDeadPaths(const PathSet & live);
+ live set). The value `minAge' specifies the minimum age in seconds
+ for an unreachable file to be considered dead (0 meaning that any
+ unreachable file is dead). */
+PathSet findDeadPaths(const PathSet & live, time_t minAge);
#endif /* !__GC_H */
diff --git a/src/nix-store/main.cc b/src/nix-store/main.cc
index 7bc8565d2..e9948c7cf 100644
--- a/src/nix-store/main.cc
+++ b/src/nix-store/main.cc
@@ -212,17 +212,22 @@ static void opIsValid(Strings opFlags, Strings opArgs)
static void opGC(Strings opFlags, Strings opArgs)
{
- if (opFlags.size() != 1) throw UsageError("missing flag");
- if (!opArgs.empty())
- throw UsageError("no arguments expected");
-
/* Do what? */
- string flag = opFlags.front();
enum { soPrintLive, soPrintDead, soDelete } subOp;
- if (flag == "--print-live") subOp = soPrintLive;
- else if (flag == "--print-dead") subOp = soPrintDead;
- else if (flag == "--delete") subOp = soDelete;
- else throw UsageError(format("bad sub-operation `%1%' in GC") % flag);
+ time_t minAge = 0;
+ for (Strings::iterator i = opFlags.begin();
+ i != opFlags.end(); ++i)
+ if (*i == "--print-live") subOp = soPrintLive;
+ else if (*i == "--print-dead") subOp = soPrintDead;
+ else if (*i == "--delete") subOp = soDelete;
+ else if (*i == "--min-age") {
+ if (opArgs.size() == 0)
+ throw UsageError("`--min-age' requires an argument");
+ istringstream st(opArgs.front());
+ st >> minAge;
+ if (!st) throw Error("number expected");
+ }
+ else throw UsageError(format("bad sub-operation `%1%' in GC") % *i);
Paths roots;
while (1) {
@@ -240,7 +245,7 @@ static void opGC(Strings opFlags, Strings opArgs)
return;
}
- PathSet dead = findDeadPaths(live);
+ PathSet dead = findDeadPaths(live, minAge * 3600);
if (subOp == soPrintDead) {
for (PathSet::iterator i = dead.begin(); i != dead.end(); ++i)