diff --git a/src/libstore/build.cc b/src/libstore/build.cc index a0924d751..9a6d72c32 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -1814,7 +1814,7 @@ void DerivationGoal::computeClosure() rivisionMapping[statePath] = commitStatePathTxn(txn, statePath); //Save the new revision - setStateRevisionsTxn(txn, rivisionMapping); + setStateRevisionsTxn(txn, rivisionMapping, statePath, "Initial build revision."); //Shared state Path sharedState = drv.stateOutputs.find("state")->second.sharedState; diff --git a/src/libstore/db.cc b/src/libstore/db.cc index 3d05e1786..9a280c3b0 100644 --- a/src/libstore/db.cc +++ b/src/libstore/db.cc @@ -617,9 +617,12 @@ bool Database::queryStateReferences(const Transaction & txn, TableId references_ return queryStrings(txn, references_table, key, references); //now that we have the key, we can query the references } -void Database::setStateRevisions(const Transaction & txn, TableId revisions_table, TableId snapshots_table, - const RevisionClosure & revisions) +void Database::setStateRevisions(const Transaction & txn, TableId revisions_table, TableId revisions_comments, + TableId snapshots_table, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment) { + if( !isStatePath(rootStatePath) ) //weak check on statePath + throw Error(format("StatePath '%1%' is not a statepath") % rootStatePath); + int ts = getTimeStamp(); //Insert all ss_epochs into snapshots_table with the current ts. @@ -637,6 +640,7 @@ void Database::setStateRevisions(const Transaction & txn, TableId revisions_tabl Path statePath = (*i).first; int revision = getNewRevisionNumber(txn, revisions_table, statePath); //get a new revision number + string key = mergeToDBKey(statePath, revision); //get all its requisites @@ -650,6 +654,16 @@ void Database::setStateRevisions(const Transaction & txn, TableId revisions_tabl data.push_back(mergeToDBKey(*j, ts)); setStrings(txn, revisions_table, key, data, false); //The false makes sure also empty revisions are set + + //save the date and comments + Strings metadata; + metadata.push_back(int2String(ts)); + if(statePath == rootStatePath) + metadata.push_back(comment); + else + metadata.push_back("Part of the snashot closure for " + rootStatePath); + setStrings(txn, revisions_comments, key, metadata); + } } @@ -720,8 +734,8 @@ bool Database::queryStateRevisions(const Transaction & txn, TableId revisions_ta } //TODO include comments into revisions? -bool Database::queryAvailableStateRevisions(const Transaction & txn, TableId revisions_table, - const Path & statePath, RevisionNumbers & revisions) +bool Database::queryAvailableStateRevisions(const Transaction & txn, TableId revisions_table, TableId revisions_comments, + const Path & statePath, RevisionInfos & revisions) { Strings keys; enumTable(txn, revisions_table, keys); //get all revisions @@ -734,7 +748,19 @@ bool Database::queryAvailableStateRevisions(const Transaction & txn, TableId rev Path getStatePath; int getRevision; splitDBKey(*i, getStatePath, getRevision); - revisions.push_back(getRevision); + + //save the date and comments + RevisionInfo rev; + Strings metadata; + queryStrings(txn, revisions_comments, *i, metadata); + unsigned int ts; + bool succeed = string2UnsignedInt(*(metadata.begin()), ts); + if(!succeed) + throw Error(format("Malformed timestamp in the revisions-comments table of path '%1%'") % *i); + rev.timestamp = ts; + metadata.pop_front(); + rev.comment = *(metadata.begin()); + revisions[getRevision] = rev; } if(revisions.empty()) diff --git a/src/libstore/db.hh b/src/libstore/db.hh index 36d7e6c97..a15ab22c5 100644 --- a/src/libstore/db.hh +++ b/src/libstore/db.hh @@ -114,16 +114,16 @@ public: const Path & statePath, Strings & references, int revision = -1, int timestamp = -1); /* Set the revision number of the statePath and the revision numbers of all state paths in the references closure */ - void setStateRevisions(const Transaction & txn, TableId revisions_table, TableId snapshots_table, - const RevisionClosure & revisions); + void setStateRevisions(const Transaction & txn, TableId revisions_table, TableId revisions_comments, + TableId snapshots_table, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment); /* Returns all the revision numbers of the state references closure of the given state path */ bool queryStateRevisions(const Transaction & txn, TableId revisions_table, TableId snapshots_table, const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, int root_revision = -1); /* Returns all available revision numbers of the given state path */ - bool queryAvailableStateRevisions(const Transaction & txn, TableId revisions_table, - const Path & statePath, RevisionNumbers & revisions); + bool queryAvailableStateRevisions(const Transaction & txn, TableId revisions_table, TableId revisions_comments, + const Path & statePath, RevisionInfos & revisions); diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 4c0b5917c..8b6bf62cc 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -135,6 +135,11 @@ static TableId dbStateInfo = 0; */ static TableId dbStateRevisions = 0; +/* + * A additional table to store comments for revisions + */ +static TableId dbStateRevisionsComments = 0; + /* dbStateSnapshots :: StatePath -> RevisionNumbers * * This table stores the snapshot numbers the sub state files/folders @@ -238,6 +243,7 @@ LocalStore::LocalStore(bool reserveSpace) dbStateComponentReferences = nixDB.openTable("references_s_c"); dbStateStateReferences = nixDB.openTable("references_s_s"); dbStateRevisions = nixDB.openTable("staterevisions"); + dbStateRevisionsComments = nixDB.openTable("staterevisions_comments"); dbStateSnapshots = nixDB.openTable("stateSnapshots"); dbSharedState = nixDB.openTable("sharedState"); dbSolidStateReferences = nixDB.openTable("references_solid_c_s"); /* The contents of this table is included in references_c_s */ @@ -1674,14 +1680,14 @@ void queryAllValidPaths(const Transaction & txn, PathSet & allComponentPaths, Pa } -void setStateRevisionsTxn(const Transaction & txn, const RevisionClosure & revisions) +void setStateRevisionsTxn(const Transaction & txn, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment) { - nixDB.setStateRevisions(txn, dbStateRevisions, dbStateSnapshots, revisions); + nixDB.setStateRevisions(txn, dbStateRevisions, dbStateRevisionsComments, dbStateSnapshots, revisions, rootStatePath, comment); } -void LocalStore::setStateRevisions(const RevisionClosure & revisions) +void LocalStore::setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment) { - nix::setStateRevisionsTxn(noTxn, revisions); + nix::setStateRevisionsTxn(noTxn, revisions, rootStatePath, comment); } bool queryStateRevisionsTxn(const Transaction & txn, const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const int revision) @@ -1694,12 +1700,12 @@ bool LocalStore::queryStateRevisions(const Path & statePath, RevisionClosure & r return nix::queryStateRevisionsTxn(noTxn, statePath, revisions, timestamps, revision); } -bool queryAvailableStateRevisionsTxn(const Transaction & txn, const Path & statePath, RevisionNumbers & revisions) +bool queryAvailableStateRevisionsTxn(const Transaction & txn, const Path & statePath, RevisionInfos & revisions) { - return nixDB.queryAvailableStateRevisions(txn, dbStateRevisions, statePath, revisions); + return nixDB.queryAvailableStateRevisions(txn, dbStateRevisions, dbStateRevisionsComments, statePath, revisions); } -bool LocalStore::queryAvailableStateRevisions(const Path & statePath, RevisionNumbers & revisions) +bool LocalStore::queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions) { return nix::queryAvailableStateRevisionsTxn(noTxn, statePath, revisions); } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 8fd097261..73290c0b4 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -100,11 +100,11 @@ public: void storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool & withComponents, const bool & withState, const int revision); - void setStateRevisions(const RevisionClosure & revisions); + void setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment); bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const int revision); - bool queryAvailableStateRevisions(const Path & statePath, RevisionNumbers & revisions); + bool queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions); void commitStatePath(const Path & statePath); }; @@ -226,14 +226,12 @@ void queryXReferencesTxn(const Transaction & txn, const Path & path, PathSet & r void setStateComponentReferencesTxn(const Transaction & txn, const Path & statePath, const Strings & references, int revision, int timestamp); void setStateStateReferencesTxn(const Transaction & txn, const Path & statePath, const Strings & references, int revision, int timestamp); - - void queryReferrersTxn(const Transaction & txn, const Path & storePath, PathSet & referrers, const int revision); void queryStateReferrersTxn(const Transaction & txn, const Path & storePath, PathSet & stateReferrers, const int revision); Path queryStatePathDrvTxn(const Transaction & txn, const Path & statePath); void storePathRequisitesTxn(const Transaction & txn, const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool & withComponents, const bool & withState, const int revision); -void setStateRevisionsTxn(const Transaction & txn, const RevisionClosure & revisions); +void setStateRevisionsTxn(const Transaction & txn, const RevisionClosure & revisions, const Path & rootStatePath, const string & comment); bool isValidPathTxn(const Transaction & txn, const Path & path); bool isValidStatePathTxn(const Transaction & txn, const Path & path); diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 53b2dc0c3..48982cab6 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -469,7 +469,7 @@ void RemoteStore::storePathRequisites(const Path & storeOrstatePath, const bool } //TODO -void RemoteStore::setStateRevisions(const RevisionClosure & revisions) +void RemoteStore::setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment) { } @@ -481,7 +481,7 @@ bool RemoteStore::queryStateRevisions(const Path & statePath, RevisionClosure & } //TODO -bool RemoteStore::queryAvailableStateRevisions(const Path & statePath, RevisionNumbers & revisions) +bool RemoteStore::queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions) { return false; } diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 911117f50..9a3f655cb 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -88,11 +88,11 @@ public: void storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool & withComponents, const bool & withState, const int revision); - void setStateRevisions(const RevisionClosure & revisions); + void setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment); bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const int revision); - bool queryAvailableStateRevisions(const Path & statePath, RevisionNumbers & revisions); + bool queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions); void commitStatePath(const Path & statePath); diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 3742e6608..a7c238dbf 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -220,13 +220,13 @@ public: virtual void storePathRequisites(const Path & storeOrstatePath, const bool includeOutputs, PathSet & paths, const bool & withComponents, const bool & withState, const int revision) = 0; /* TODO */ - virtual void setStateRevisions(const RevisionClosure & revisions) = 0; + virtual void setStateRevisions(const RevisionClosure & revisions, const Path & rootStatePath, const string & comment) = 0; /* TODO */ virtual bool queryStateRevisions(const Path & statePath, RevisionClosure & revisions, RevisionClosureTS & timestamps, const int revision) = 0; /* TODO */ - virtual bool queryAvailableStateRevisions(const Path & statePath, RevisionNumbers & revisions) = 0; + virtual bool queryAvailableStateRevisions(const Path & statePath, RevisionInfos & revisions) = 0; /* TODO */ virtual void commitStatePath(const Path & statePath) = 0; diff --git a/src/libutil/types.hh b/src/libutil/types.hh index 070b9ecf5..520fced0d 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -60,6 +60,12 @@ typedef set PathSet; //state types typedef list RevisionNumbers; //the Strings (list) of StateReferences and this list are connected by position +struct RevisionInfo +{ + string comment; + unsigned int timestamp; +}; +typedef map RevisionInfos; typedef map Snapshots; //Automatically sorted on Path :) typedef map RevisionClosure; typedef map RevisionClosureTS; diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 65dd0ad3f..1f995241f 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -1248,7 +1248,19 @@ void ensureDirExists(const Path & path) Strings p_args; p_args.push_back("-p"); p_args.push_back(path); - runProgram_AndPrintOutput("mkdir", true, p_args, "mkdir"); //TODO ensurePath + runProgram_AndPrintOutput("mkdir", true, p_args, "mkdir"); //TODO ensurePath } +string padd(const string & s, char c , unsigned int size, bool front) +{ + string ss = s; + while (ss.length() < size){ + if(front) + ss = c + ss; + else + ss = ss + c; + } + return ss; +} + } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index ac6121b5b..692437109 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -318,9 +318,10 @@ PathSet pathSets_union(const PathSet & paths1, const PathSet & paths2); /* TODO */ void pathSets_difference(const PathSet & oldpaths, const PathSet & newpaths, PathSet & addedpaths, PathSet & removedpaths); - void ensureDirExists(const Path & path); +string padd(const string & s, char c , unsigned int size, bool front = false); + } #endif /* !__UTIL_H */ diff --git a/src/nix-state/nix-state.cc b/src/nix-state/nix-state.cc index da5640c2a..888801533 100644 --- a/src/nix-state/nix-state.cc +++ b/src/nix-state/nix-state.cc @@ -24,6 +24,7 @@ typedef void (* Operation) (Strings opFlags, Strings opArgs); //two global variables string stateIdentifier; string username; +string comment; int revision_arg; bool scanforReferences = false; bool only_commit = false; @@ -55,7 +56,7 @@ Derivation getDerivation(const string & fullPath, const string & program_args, s componentPath = fullPath.substr(0, pos + nixStore.size() + 1); binary = fullPath.substr(pos + nixStore.size() + 1, fullPath.size()); - if( ! (store->isValidPath(componentPath) || store->isValidStatePath(componentPath)) ) + if( !(store->isValidPath(componentPath) || store->isValidStatePath(componentPath)) ) throw UsageError(format("Path '%1%' is not a valid state or store path") % componentPath); //Check if path is statepath @@ -173,35 +174,59 @@ PathSet getAllStateDerivationsRecursively(const Path & storePath, const int revi return derivations; } -//TODO also allow statePaths as input? static void queryAvailableStateRevisions(Strings opFlags, Strings opArgs) { - Path componentPath; - Path statePath; - string binary; - string derivationPath; - bool isStateComponent; - string program_args; - Derivation drv = getDerivation_andCheckArgs(opFlags, opArgs, componentPath, statePath, binary, derivationPath, isStateComponent, program_args); + Path statePath; + + if(store->isValidStatePath(*(opArgs.begin()))) + statePath = *(opArgs.begin()); + else{ + Path componentPath; + string binary; + string derivationPath; + bool isStateComponent; + string program_args; + Derivation drv = getDerivation_andCheckArgs(opFlags, opArgs, componentPath, statePath, binary, derivationPath, isStateComponent, program_args); + } - RevisionNumbers revisions; + RevisionInfos revisions; bool notEmpty = store->queryAvailableStateRevisions(statePath, revisions); - if(!notEmpty){ //can this happen? + + if(!notEmpty){ printMsg(lvlError, format("No revisions yet for: %1%") % statePath); return; } //Sort ourselfes to create a nice output vector revisions_sort; - for (RevisionNumbers::iterator i = revisions.begin(); i != revisions.end(); ++i) - revisions_sort.push_back(*i); + int highestrev; + for (RevisionInfos::iterator i = revisions.begin(); i != revisions.end(); ++i){ + int rev = (*i).first; + revisions_sort.push_back(rev); + if(rev > highestrev) + highestrev = rev; + } sort(revisions_sort.begin(), revisions_sort.end()); - + + int max_size = int2String(highestrev).length(); for (vector::iterator i = revisions_sort.begin(); i != revisions_sort.end(); ++i) - printMsg(lvlError, format("Available Revision: %1% with date .....") % *i); - - + { + int rev = *i; + string rev_s = padd(int2String(rev), '0' , max_size, true); //pad revisions with a 0 + unsigned int ts = revisions[rev].timestamp; + time_t time = atoi(unsignedInt2String(ts).c_str()); + string human_date = ctime(&time); + human_date.erase(human_date.find("\n",0),1); //remove newline + string comment = revisions[rev].comment; + + + + if(trim(comment) != "") + printMsg(lvlError, format("Rev. %1% @ %2% (%3%) -- %4%") % rev_s % human_date % ts % comment); + else + printMsg(lvlError, format("Rev. %1% @ %2% (%3%)") % rev_s % human_date % ts); + } } static void revertToRevision(Strings opFlags, Strings opArgs) @@ -482,7 +507,7 @@ static void opRunComponent(Strings opFlags, Strings opArgs) rivisionMapping[*i] = commitStatePathTxn(txn, *i); //Save new revisions - setStateRevisionsTxn(txn, rivisionMapping); + setStateRevisionsTxn(txn, rivisionMapping, root_statePath, comment); //Commit transaction //txn.commit(); @@ -697,7 +722,8 @@ void run(Strings args) stateIdentifier = arg.substr(13,arg.length()); else if (arg.substr(0,7) == "--user=") username = arg.substr(7,arg.length()); - + else if (arg.substr(0,10) == "--comment=") + comment = arg.substr(10,arg.length()); else opArgs.push_back(arg);