From ca5fc7c582f0c0aea5d790eb71f898acfba5278e Mon Sep 17 00:00:00 2001 From: Wouter den Breejen Date: Sun, 8 Jul 2007 19:02:08 +0000 Subject: [PATCH] major update --- scripts/Makefile.am | 7 +- scripts/nix-readrevisions.sh | 13 +++ scripts/nix-readrevisions.sh.in | 13 +++ scripts/nix-restorerevision.sh | 13 +++ scripts/nix-restorerevision.sh.in | 13 +++ scripts/nix-statecommit.sh | 174 ++++++++++++++++++++++++++++++ src/libstore/db.cc | 130 +++++++++++++++++++--- src/libstore/db.hh | 13 ++- src/libstore/local-store.cc | 50 ++++++++- src/libstore/local-store.hh | 6 ++ src/libstore/remote-store.cc | 16 +++ src/libstore/remote-store.hh | 6 ++ src/libstore/store-api.hh | 9 ++ src/libutil/types.hh | 1 + src/nix-state/nix-state.cc | 166 ++++++++++++++++++++-------- 15 files changed, 562 insertions(+), 68 deletions(-) create mode 100755 scripts/nix-readrevisions.sh create mode 100755 scripts/nix-readrevisions.sh.in create mode 100755 scripts/nix-restorerevision.sh create mode 100755 scripts/nix-restorerevision.sh.in create mode 100755 scripts/nix-statecommit.sh diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 6937a8d1c..e387220c9 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -4,7 +4,7 @@ bin_SCRIPTS = nix-collect-garbage \ nix-pack-closure nix-unpack-closure \ nix-copy-closure -noinst_SCRIPTS = nix-profile.sh generate-patches.pl find-runtime-roots.pl nix-statecommit.sh nix-readrevisions.sh +noinst_SCRIPTS = nix-profile.sh generate-patches.pl find-runtime-roots.pl nix-statecommit.sh nix-readrevisions.sh nix-restorerevision.sh nix-pull nix-push: readmanifest.pm readconfig.pm download-using-manifests.pl @@ -19,6 +19,7 @@ install-exec-local: readmanifest.pm download-using-manifests.pl find-runtime-roo $(INSTALL_PROGRAM) generate-patches.pl $(DESTDIR)$(libexecdir)/nix $(INSTALL_PROGRAM) nix-statecommit.sh $(DESTDIR)$(libexecdir)/nix $(INSTALL_PROGRAM) nix-readrevisions.sh $(DESTDIR)$(libexecdir)/nix + $(INSTALL_PROGRAM) nix-restorerevision.sh $(DESTDIR)$(libexecdir)/nix $(INSTALL) -d $(DESTDIR)$(sysconfdir)/nix include ../substitute.mk @@ -36,5 +37,5 @@ EXTRA_DIST = nix-collect-garbage.in \ nix-copy-closure.in \ find-runtime-roots.pl.in \ nix-statecommit.sh.in \ - nix-readrevisions.sh - + nix-readrevisions.sh.in \ + nix-restorerevision.sh.in diff --git a/scripts/nix-readrevisions.sh b/scripts/nix-readrevisions.sh new file mode 100755 index 000000000..bdbb449dc --- /dev/null +++ b/scripts/nix-readrevisions.sh @@ -0,0 +1,13 @@ +#! /bin/sh -e + +svnbin=$1 +repos=$2 + +if [ "$#" != 2 ] ; then + echo "Incorrect number of arguments" + exit 1; +fi + +$svnbin info $repos | sed -n '/^Revision: /p' | sed 's/Revision: //' + +# | tr -d "\12" diff --git a/scripts/nix-readrevisions.sh.in b/scripts/nix-readrevisions.sh.in new file mode 100755 index 000000000..bdbb449dc --- /dev/null +++ b/scripts/nix-readrevisions.sh.in @@ -0,0 +1,13 @@ +#! /bin/sh -e + +svnbin=$1 +repos=$2 + +if [ "$#" != 2 ] ; then + echo "Incorrect number of arguments" + exit 1; +fi + +$svnbin info $repos | sed -n '/^Revision: /p' | sed 's/Revision: //' + +# | tr -d "\12" diff --git a/scripts/nix-restorerevision.sh b/scripts/nix-restorerevision.sh new file mode 100755 index 000000000..60cea74bd --- /dev/null +++ b/scripts/nix-restorerevision.sh @@ -0,0 +1,13 @@ +#! /bin/sh -e + +svnbin=$1 +torevision=$2 +repos=$3 + +if [ "$#" != 3 ] ; then + echo "Incorrect number of arguments" + exit 1; +fi + +$svnbin merge -r HEAD:$2 $3 + diff --git a/scripts/nix-restorerevision.sh.in b/scripts/nix-restorerevision.sh.in new file mode 100755 index 000000000..60cea74bd --- /dev/null +++ b/scripts/nix-restorerevision.sh.in @@ -0,0 +1,13 @@ +#! /bin/sh -e + +svnbin=$1 +torevision=$2 +repos=$3 + +if [ "$#" != 3 ] ; then + echo "Incorrect number of arguments" + exit 1; +fi + +$svnbin merge -r HEAD:$2 $3 + diff --git a/scripts/nix-statecommit.sh b/scripts/nix-statecommit.sh new file mode 100755 index 000000000..7fe55d628 --- /dev/null +++ b/scripts/nix-statecommit.sh @@ -0,0 +1,174 @@ +#! /bin/sh +# we cant do a -e since ... svn can fail ??? TODO... + +#check if there are enough arguments, if not, exit with an error + +debug=""; #set to "" for no debugging, set to "echo " to debug the commands + +if [ "$#" != 5 ] && [ "$#" != 6 ] ; then + echo "Incorrect number of arguments" + exit 1; +fi + +if [ "$deletesvn" != "" ] && [ "$deletesvn" != "1" ]; then + echo "The last argument (DELETE svn folders) must be either empty (recommended) or 1" + exit 1; +fi + +svnbin=$1 +subversionedpaths=( $2 ) #arrays +subversionedpathsCommitBools=( $3 ) +nonversionedpaths=( $4 ) +checkouts=( $5 ) +deletesvn=$6 #this flag can be set to 1 to DELETE all .svn folders and NOT commit + + +if [ "$debug" != "" ] ; then + echo svnbin: $svnbin + echo subversionedpaths: ${subversionedpaths[@]} + echo subversionedpathsCommitBools: ${subversionedpathsCommitBools[@]} + echo nonversionedpaths: ${nonversionedpaths[@]} + echo checkouts: ${checkouts[@]} + echo deletesvn: $deletesvn +fi + +# +# +# +# +function subversionSingleStateDir { + + excludelist=( "." ".." ".svn" ); + + cd $1; + #echo cd $1; + + empty=$(ls) + + if [ "$empty" = "" ] ; then + allsubitems=(); #no subfiles / dirs + else + allsubitems=( $(echo *) $(echo .*) ) #there are subfiles / dirs,also adds hidden items + fi + + for subitem in ${allsubitems[@]} + do + if [ "$subitem" = ".svn" ]; then + allsubitems=( $(svn -N stat | sed -n '/^?/p' | sed 's/? //' | tr -d "\12") ) #there are subfiles, and theyre already versioned + + if [ "$deletesvn" = "1" ]; then + rm -rf .svn/ + fi + fi + done + + #echo "Allsubitems ${allsubitems[@]}" + + subitems=(); + for subitem in ${allsubitems[@]} #add all, were going to exlucde explicity stated direct versioned-subdirs or explicity stated nonversioned-subdirs + do #this is only to prevent some warnings, ultimately we would like svn add to have a option 'exclude dirs' + + exclude=0; + + for excl in ${excludelist[@]} #check if the subitem is in the list of excluded dirs + do + if [ "$excl" = "$subitem" ]; then + exclude=1; + #echo "exclude $excl" + fi + done + + subitem="$(pwd)/$subitem"; #create the full path + + if test -d $subitem; then #the subitem (file or a dir) may be a dir, so we add a / to the end + subitem="$subitem/"; + fi + + for svnp in ${subversionedpaths[@]} #check if the subitem is in the list of subverioned paths + do + if [ "$svnp" = "$subitem" ]; then + exclude=1; + #echo "exclude versioned $svnp" + fi + done + + for nonvp in ${nonversionedpaths[@]} #check if the subitem is in the list of dirs that aren't supposed to be versioned + do + if [ "$nonvp" = "$subitem" ]; then + exclude=1; + #echo "exclude nonversioned $svnp" + fi + done + + if [ $exclude = 0 ]; then #Exclude the subitem if nessecary + subitems[${#subitems[*]}]=$subitem + fi + done + + #echo ${subitems[@]} + + for item in ${subitems[@]} + do + if test -d $item; then #add or go recursive subitems + if [ "$deletesvn" != "1" ]; then + $debug svn -N add $item #NON recursively add the dir + fi + subversionSingleStateDir $item + else + if [ "$deletesvn" != "1" ]; then + $debug svn add $item + fi + fi + done + +} + +# +# +# +# + +i=0 +i_checkout=0 +for path in ${subversionedpaths[@]} +do + if test -d $path; then #if the dir doesnt exist, than we dont hav to do anything + cd $path; + + checkoutcommand=""; #HACK: I cant seem to find a way for bash to parse a 2 dimensional string array as argument, so we use a 1-d array with '|' as seperator + while true; do + if [ "${checkouts[$i_checkout]}" = "|" ]; then + let "i_checkout+=1" + break + fi + checkoutcommand="${checkoutcommand} ${checkouts[$i_checkout]}"; + let "i_checkout+=1" + done + + if ! test -d "${path}.svn/"; then #if the dir exists but is not yet an svn dir: checkout repos, if it doenst exits (is removed or something) than we dont do anything + if [ "$deletesvn" != "1" ]; then + $debug $checkoutcommand; + fi + fi + + if [ "${subversionedpathsCommitBools[$i]}" = "true" ]; then #Check if we need to commit this folder + + echo "Entering $path" + + subversionSingleStateDir $path; + + cd $path #now that everything is added we go back to the 'root' path and commit + if [ "$deletesvn" != "1" ]; then + $debug svn -m "" commit; + fi + fi + + cd - &> /dev/null; + let "i+=1" + fi +done + + + + + diff --git a/src/libstore/db.cc b/src/libstore/db.cc index 82942882c..c1a2842a5 100644 --- a/src/libstore/db.cc +++ b/src/libstore/db.cc @@ -456,22 +456,45 @@ void Database::splitStatePathRevision(const Path & revisionedStatePath, Path & s { string prefix = "-REV-"; - int pos = revisionedStatePath.find_last_not_of(prefix); - statePath = revisionedStatePath.substr(0, pos - prefix.length()); - //printMsg(lvlError, format("'%1%' '%2%'") % statePath % revisionedStatePath.substr(pos, revisionedStatePath.size())); - bool succeed = string2Int(revisionedStatePath.substr(pos, revisionedStatePath.size()), revision); + int pos = revisionedStatePath.find_last_of(prefix); + statePath = revisionedStatePath.substr(0, pos - prefix.length() + 1); + //printMsg(lvlError, format("'%2%' - '%1%'") % revisionedStatePath.substr(pos+1, revisionedStatePath.length()) % int2String(pos)); + bool succeed = string2Int(revisionedStatePath.substr(pos+1, revisionedStatePath.length()), revision); if(!succeed) throw Error(format("Malformed revision value of path '%1%'") % revisionedStatePath); } +int Database::getNewRevisionNumber(const Transaction & txn, TableId table, + const Path & statePath) +{ + //query + string data; + bool notEmpty = queryString(txn, table, statePath, data); + + if(!notEmpty){ + setString(txn, table, statePath, int2String(1)); + return 1; + } + + int revision; + bool succeed = string2Int(data, revision); + if(!succeed) + throw Error(format("Malformed revision counter value of path '%1%'") % statePath); + + revision++; + setString(txn, table, statePath, int2String(revision)); + + return revision; +} + void Database::setStateReferences(const Transaction & txn, TableId table, - const Path & statePath, const int revision, const Strings & references) + const Path & statePath, const Strings & references, int revision) { //printMsg(lvlError, format("setStateReferences/Referrers %1%") % table); if(revision == -1) - throw Error("-1 is not a valid revision value for SET-references/referrers"); + revision = getNewRevisionNumber(txn, table, statePath); /* for (Strings::const_iterator i = references.begin(); i != references.end(); ++i) @@ -490,6 +513,31 @@ void Database::setStateReferences(const Transaction & txn, TableId table, setStrings(txn, table, key, references); } +bool Database::lookupHighestRevivison(const Strings & keys, const Path & statePath, string & key) +{ + int highestRev = -1; + + //Lookup which key we need + for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) { + + if((*i).substr(0, statePath.length()) != statePath || (*i).length() == statePath.length()) //dont check the new-revision key or other keys + continue; + + //printMsg(lvlError, format("'%1%' - '%2%'") % *i % statePath); + Path getStatePath; + int getRevision; + splitStatePathRevision(*i, getStatePath, getRevision); + if(getRevision > highestRev) + highestRev = getRevision; + } + + if(highestRev == -1) //no records found (TODO throw error?) + return false; + + key = makeStatePathRevision(statePath, highestRev); //final key that matches revision + statePath + return true; +} + bool Database::queryStateReferences(const Transaction & txn, TableId table, const Path & statePath, Strings & references, int revision) { @@ -498,6 +546,16 @@ bool Database::queryStateReferences(const Transaction & txn, TableId table, Strings keys; enumTable(txn, table, keys); //get all revisions + string key; + if(revision == -1){ + bool foundsomething = lookupHighestRevivison(keys, statePath, key); + if(!foundsomething) + return false; + } + else + key = makeStatePathRevision(statePath, revision); + + /* ///////////////?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! create function //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// TODO string key = ""; //final key that matches revision + statePath @@ -529,6 +587,7 @@ bool Database::queryStateReferences(const Transaction & txn, TableId table, if(revision == -1) key = makeStatePathRevision(statePath, highestRev); + */ return queryStrings(txn, table, key, references); //now that we have the key, we can query the references } @@ -551,12 +610,32 @@ bool Database::queryStateReferrers(const Transaction & txn, TableId table, void Database::setStateRevisions(const Transaction & txn, TableId table, - const Path & statePath, const int revision, const RevisionNumbersClosure & revisions) + const Path & statePath, const RevisionNumbersSetClosure & revisions, int revision) { + if(revision == -1) + revision = getNewRevisionNumber(txn, table, statePath); + + //Sort based on statePath to RevisionNumbersClosure + RevisionNumbersClosure sorted_revisions; + vector sortedStatePaths; + for (RevisionNumbersSetClosure::const_iterator i = revisions.begin(); i != revisions.end(); ++i) + sortedStatePaths.push_back((*i).first); + sort(sortedStatePaths.begin(), sortedStatePaths.end()); + for (vector::const_iterator i = sortedStatePaths.begin(); i != sortedStatePaths.end(); ++i) + sorted_revisions.push_back(revisions.at(*i)); + + ////////////////// + for (vector::const_iterator i = sortedStatePaths.begin(); i != sortedStatePaths.end(); ++i){ + printMsg(lvlError, format("Insert: %1%") % *i); + for (RevisionNumbers::const_iterator e = (revisions.at(*i)).begin(); e != (revisions.at(*i)).end(); ++e) + printMsg(lvlError, format("Rev: %1%") % *e); + } + ////////////////// + //Pack the data into Strings const string seperator = "|"; Strings data; - for (RevisionNumbersClosure::const_iterator i = revisions.begin(); i != revisions.end(); ++i) { + for (RevisionNumbersClosure::const_iterator i = sorted_revisions.begin(); i != sorted_revisions.end(); ++i) { RevisionNumbers revisionNumbers = *i; string packedNumbers = ""; for (RevisionNumbers::iterator j = revisionNumbers.begin(); j != revisionNumbers.end(); ++j) @@ -580,6 +659,16 @@ bool Database::queryStateRevisions(const Transaction & txn, TableId table, Strings keys; enumTable(txn, table, keys); //get all revisions + string key; + if(revision == -1){ + bool foundsomething = lookupHighestRevivison(keys, statePath, key); + if(!foundsomething) + return false; + } + else + key = makeStatePathRevision(statePath, revision); + + /* //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// string key = ""; //final key that matches revision + statePath int highestRev = -1; @@ -610,7 +699,8 @@ bool Database::queryStateRevisions(const Transaction & txn, TableId table, if(revision == -1) key = makeStatePathRevision(statePath, highestRev); - + */ + Strings data; bool succeed = queryStrings(txn, table, key, data); //now that we have the key, we can query the references @@ -632,14 +722,28 @@ bool Database::queryStateRevisions(const Transaction & txn, TableId table, return succeed; } -bool Database::queryAllStateRevisions(const Transaction & txn, TableId table, +//TODO include comments into revisions? +bool Database::queryAvailableStateRevisions(const Transaction & txn, TableId table, const Path & statePath, RevisionNumbers & revisions) { - //TODO + Strings keys; + enumTable(txn, table, keys); //get all revisions - //LIST OF x ..... y which revisions are available for a rollback + for (Strings::const_iterator i = keys.begin(); i != keys.end(); ++i) { - return false; + if((*i).substr(0, statePath.length()) != statePath || (*i).length() == statePath.length()) //dont check the new-revision key or other keys + continue; + + Path getStatePath; + int getRevision; + splitStatePathRevision(*i, getStatePath, getRevision); + revisions.push_back(getRevision); + } + + if(revisions.empty()) + return false; + else + return true; } } diff --git a/src/libstore/db.hh b/src/libstore/db.hh index 98096a2f9..8d7c053cc 100644 --- a/src/libstore/db.hh +++ b/src/libstore/db.hh @@ -60,6 +60,13 @@ private: void open2(const string & path, bool removeOldEnv); + /* TODO */ + bool lookupHighestRevivison(const Strings & keys, const Path & statePath, string & key); + + /* TODO */ + int getNewRevisionNumber(const Transaction & txn, TableId table, const Path & statePath); + + public: Database(); ~Database(); @@ -98,7 +105,7 @@ public: /* Set the stateReferences for a specific revision (and older until the next higher revision number in the table) */ void setStateReferences(const Transaction & txn, TableId table, - const Path & statePath, const int revision, const Strings & references); + const Path & statePath, const Strings & references, int revision = -1); /* Returns the references for a specific revision (and older until the next higher revision number in the table) */ bool queryStateReferences(const Transaction & txn, TableId table, @@ -110,14 +117,14 @@ public: /* 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 table, - const Path & statePath, const int revision, const RevisionNumbersClosure & revisions); + const Path & statePath, const RevisionNumbersSetClosure & revisions, int revision = -1); /* Returns all the revision numbers of the state references closure of the given state path */ bool queryStateRevisions(const Transaction & txn, TableId table, const Path & statePath, RevisionNumbersClosure & revisions, int revision = -1); /* Returns all available revision numbers of the given state path */ - bool queryAllStateRevisions(const Transaction & txn, TableId table, + bool queryAvailableStateRevisions(const Transaction & txn, TableId table, const Path & statePath, RevisionNumbers & revisions); }; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 689ef6497..36ade6cee 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -116,6 +116,13 @@ static TableId dbStateCounters = 0; */ static TableId dbStateInfo = 0; +/* dbStateRevisions :: StatePath -> RevisionNumbersClosure + + This table lists the statepaths + recursive (indirect) references and the revision numbers of their repositorys + +*/ +static TableId dbStateRevisions = 0; + bool Substitute::operator == (const Substitute & sub) const { return program == sub.program @@ -189,7 +196,8 @@ LocalStore::LocalStore(bool reserveSpace) dbComponentStateReferrers = nixDB.openTable("referrers_c_s", true); dbStateComponentReferrers = nixDB.openTable("referrers_s_c", true); dbStateStateReferrers = nixDB.openTable("referrers_s_s", true); - + dbStateRevisions = nixDB.openTable("staterevisions", true); + int curSchema = 0; Path schemaFN = nixDBPath + "/schema"; @@ -484,8 +492,8 @@ void setReferences(const Transaction & txn, const Path & store_or_statePath, oldReferences2 = PathSet(oldStateReferences_s_c.begin(), oldStateReferences_s_c.end()); oldStateReferences2 = PathSet(oldStateReferences_s_s.begin(), oldStateReferences_s_s.end()); - nixDB.setStateReferences(txn, dbStateComponentReferences, store_or_statePath, revision, Paths(references.begin(), references.end())); - nixDB.setStateReferences(txn, dbStateStateReferences, store_or_statePath, revision, Paths(stateReferences.begin(), stateReferences.end())); + nixDB.setStateReferences(txn, dbStateComponentReferences, store_or_statePath, Paths(references.begin(), references.end()), revision); + nixDB.setStateReferences(txn, dbStateStateReferences, store_or_statePath, Paths(stateReferences.begin(), stateReferences.end()), revision); //set vars for the referrers update code below dbXComponentReferrers = dbStateComponentReferrers; @@ -1702,6 +1710,8 @@ void scanAndUpdateAllReferencesTxn(const Transaction & txn, const Path & statePa allReferencesKeys2.push_back(path); } + //TODO maybe only scan in the changeset (patch) for new references? (this will be difficult and depending on the underlying versioning system) + //Scan in for component and state references PathSet state_references = scanForReferences(statePath, PathSet(allReferencesKeys2.begin(), allReferencesKeys2.end())); PathSet state_stateReferences = scanForReferences(statePath, PathSet(allStateReferencesKeys.begin(), allStateReferencesKeys.end())); @@ -1738,6 +1748,7 @@ void scanAndUpdateAllReferencesTxn(const Transaction & txn, const Path & statePa printMsg(lvlError, format("Added state reference found!: '%1%' in state path '%2%'") % (*i) % statePath); //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + /* Register Valid again if neccesary update the extra references in a new table??? why??? @@ -1792,7 +1803,7 @@ void scanAndUpdateAllReferencesRecusively(const Transaction & txn, const Path & //call scanForAllReferences again on all newly found statePaths for (PathSet::iterator i = statePaths.begin(); i != statePaths.end(); ++i) { - //... and merge + //Scan, update, call recursively PathSet newFoundComponentReferences; PathSet newFoundStateReferences; scanAndUpdateAllReferencesTxn(txn, *i, newFoundComponentReferences, newFoundStateReferences); @@ -1809,6 +1820,37 @@ void LocalStore::scanAndUpdateAllReferencesRecusively(const Path & storeOrStateP return nix::scanAndUpdateAllReferencesRecusively(noTxn, storeOrStatePath); } +void setStateRevisions(const Transaction & txn, const Path & statePath, const RevisionNumbersSetClosure & revisions, const int revision) +{ + nixDB.setStateRevisions(txn, dbStateRevisions, statePath, revisions, revision); +} + +void LocalStore::setStateRevisions(const Path & statePath, const RevisionNumbersSetClosure & revisions, const int revision) +{ + nix::setStateRevisions(noTxn, statePath, revisions, revision); +} + +bool queryStateRevisions(const Transaction & txn, const Path & statePath, RevisionNumbersClosure & revisions, const int revision) +{ + return nixDB.queryStateRevisions(txn, dbStateRevisions, statePath, revisions, revision); +} + +bool LocalStore::queryStateRevisions(const Path & statePath, RevisionNumbersClosure & revisions, const int revision) +{ + return nix::queryStateRevisions(noTxn, statePath, revisions, revision); +} + +bool queryAvailableStateRevisions(const Transaction & txn, const Path & statePath, RevisionNumbers & revisions) +{ + return nixDB.queryAvailableStateRevisions(txn, dbStateRevisions, statePath, revisions); +} + +bool LocalStore::queryAvailableStateRevisions(const Path & statePath, RevisionNumbers & revisions) +{ + return nix::queryAvailableStateRevisions(noTxn, statePath, revisions); +} + + /* Upgrade from schema 1 (Nix <= 0.7) to schema 2 (Nix >= 0.8). */ static void upgradeStore07() diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index fe077a124..259250bfc 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -102,6 +102,12 @@ public: void scanAndUpdateAllReferencesRecusively(const Path & storeOrstatePath); + void setStateRevisions(const Path & statePath, const RevisionNumbersSetClosure & revisions, const int revision); + + bool queryStateRevisions(const Path & statePath, RevisionNumbersClosure & revisions, const int revision); + + bool queryAvailableStateRevisions(const Path & statePath, RevisionNumbers & revisions); + }; diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 10e55b4c2..c48e43bd2 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -474,6 +474,22 @@ void RemoteStore::scanAndUpdateAllReferencesRecusively(const Path & storeOrstate } +//TODO +void RemoteStore::setStateRevisions(const Path & statePath, const RevisionNumbersSetClosure & revisions, const int revision) +{ + +} +//TODO +bool RemoteStore::queryStateRevisions(const Path & statePath, RevisionNumbersClosure & revisions, const int revision) +{ + return false; +} + +//TODO +bool RemoteStore::queryAvailableStateRevisions(const Path & statePath, RevisionNumbers & revisions) +{ + return false; +} } diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 43fe6c80a..5cbfeefb9 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -89,6 +89,12 @@ public: void scanAndUpdateAllReferences(const Path & statePath); void scanAndUpdateAllReferencesRecusively(const Path & storeOrstatePath); + + void setStateRevisions(const Path & statePath, const RevisionNumbersSetClosure & revisions, const int revision); + + bool queryStateRevisions(const Path & statePath, RevisionNumbersClosure & revisions, const int revision); + + bool queryAvailableStateRevisions(const Path & statePath, RevisionNumbers & revisions); private: AutoCloseFD fdSocket; diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index da0c00fc2..741af8485 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -222,6 +222,15 @@ public: /* TODO */ virtual void scanAndUpdateAllReferencesRecusively(const Path & storeOrstatePath) = 0; + + /* TODO */ + virtual void setStateRevisions(const Path & statePath, const RevisionNumbersSetClosure & revisions, const int revision) = 0; + + /* TODO */ + virtual bool queryStateRevisions(const Path & statePath, RevisionNumbersClosure & revisions, const int revision) = 0; + + /* TODO */ + virtual bool queryAvailableStateRevisions(const Path & statePath, RevisionNumbers & revisions) = 0; }; diff --git a/src/libutil/types.hh b/src/libutil/types.hh index d162af2bb..3ee5b7cf3 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -60,6 +60,7 @@ typedef set PathSet; //state types typedef list RevisionNumbers; //the Strings (list) of StateReferences and this list are connected by position +typedef map RevisionNumbersSetClosure; //We include to the paths to sort on typedef list RevisionNumbersClosure; typedef map StateReferences; diff --git a/src/nix-state/nix-state.cc b/src/nix-state/nix-state.cc index 0e52d18fd..eb061db7a 100644 --- a/src/nix-state/nix-state.cc +++ b/src/nix-state/nix-state.cc @@ -22,6 +22,7 @@ typedef void (* Operation) (Strings opFlags, Strings opArgs); //two global variables string stateIdentifier; string username; +int revision_arg; /************************* Build time Functions ******************************/ @@ -181,50 +182,48 @@ RevisionNumbers readRevisionNumbers(const Derivation & drv) Path statePath = stateOutputs.find("state")->second.statepath; string getStateIdentifier = stateOutputs.find("state")->second.stateIdentifier; + //TODO sort based on the repository + vector sorted_repositorys; for (DerivationStateOutputDirs::const_reverse_iterator i = stateOutputDirs.rbegin(); i != stateOutputDirs.rend(); ++i){ DerivationStateOutputDir d = i->second; string thisdir = d.path; - //Get the a repository for this state location string repos = getStateReposPath("stateOutput:staterepospath", statePath, thisdir, drvName, getStateIdentifier); //this is a copy from store-state.cc + sorted_repositorys.push_back(repos); + } + sort(sorted_repositorys.begin(), sorted_repositorys.end()); + + //After the sort read the paths + for (vector::const_iterator i = sorted_repositorys.begin(); i != sorted_repositorys.end(); ++i){ + string repos = *i; + + printMsg(lvlError, format("%1%") % repos); if(IsDirectory(repos)){ Strings p_args; p_args.push_back(svnbin); p_args.push_back("file://" + repos); - string output = runProgram(nixLibexecDir + "/nix/nix-readrevisions.sh", true, p_args); - int pos = output.find("\n",0); + string output = runProgram(nixLibexecDir + "/nix/nix-readrevisions.sh", true, p_args); //run + + int pos = output.find("\n",0); //remove trailing \n output.erase(pos,1); + int revision; bool succeed = string2Int(output, revision); if(!succeed) throw Error(format("Cannot read revision number of path '%1%'") % repos); - revisions.push_back(revision); - } + revisions.push_back(revision); //insert into array + } else revisions.push_back(-1); } return revisions; } -void setAllRevisionNumbers(const PathSet & drvs) -{ - //for..... - - //DerivationStateOutputs stateOutputs = drv.stateOutputs; - //Path statePath = stateOutputs.find("state")->second.statepath; - //RevisionNumbers = readRevisionNumbers(drv); - - - - //setStateRevisions(notxn, TableId table, const Path & statePath, const int revision, const RevisionNumbersClosure & revisions); - /* - bool queryStateRevisions(const Transaction & txn, TableId table, - const Path & statePath, RevisionNumbersClosure & revisions, int revision = -1); - */ -} - -//Comment TODO +/* + * Input: store (or statePath?) + * Returns all the drv's of the statePaths (in)directly referenced. + */ PathSet getAllStateDerivationsRecursively(const Path & storePath) { //Get recursively all state paths @@ -239,16 +238,61 @@ PathSet getAllStateDerivationsRecursively(const Path & storePath) return derivations; } +//TODO also allow statePaths as input? +static void queryAvailableStateRevisions(Strings opFlags, Strings opArgs) +{ + Path componentPath; + Path statePath; + string binary; + string derivationPath; + bool isStatePath; + string program_args; + Derivation drv = getDerivation_andCheckArgs(opFlags, opArgs, componentPath, statePath, binary, derivationPath, isStatePath, program_args); + + RevisionNumbers revisions; + bool notEmpty = store->queryAvailableStateRevisions(statePath, revisions); + + if(!notEmpty){ //can this happen? + printMsg(lvlError, format("No revisions yet for: %1%") % statePath); + return; + } + + for (RevisionNumbers::iterator i = revisions.begin(); i != revisions.end(); ++i) + { + printMsg(lvlError, format("Available Revision: %1%") % int2String(*i)); + } +} + +static void revertToRevision(Strings opFlags, Strings opArgs) +{ + Path componentPath; + Path statePath; + string binary; + string derivationPath; + bool isStatePath; + string program_args; + Derivation drv = getDerivation_andCheckArgs(opFlags, opArgs, componentPath, statePath, binary, derivationPath, isStatePath, program_args); + + bool recursive = true; //TODO !!!!!!!!! + + //get dependecies (if neccecary | recusively) of all state components that need to be updated + PathSet root_drvs = getAllStateDerivationsRecursively(componentPath); + + //revision_arg + + //TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +} + static void opRunComponent(Strings opFlags, Strings opArgs) { //get the all the info of the component that is being called (we dont really use it yet) - Path componentPath; - Path statePath; - string binary; - string derivationPath; - bool isStatePath; - string program_args; - Derivation drv = getDerivation_andCheckArgs(opFlags, opArgs, componentPath, statePath, binary, derivationPath, isStatePath, program_args); + Path root_componentPath; + Path root_statePath; + string root_binary; + string root_derivationPath; + bool root_isStatePath; + string root_program_args; + Derivation root_drv = getDerivation_andCheckArgs(opFlags, opArgs, root_componentPath, root_statePath, root_binary, root_derivationPath, root_isStatePath, root_program_args); //Specifiy the SVN binarys string svnbin = nixSVNPath + "/svn"; @@ -261,8 +305,7 @@ static void opRunComponent(Strings opFlags, Strings opArgs) //svn lock ... ? //get dependecies (if neccecary | recusively) of all state components that need to be updated - PathSet drvs = getAllStateDerivationsRecursively(componentPath); - + PathSet root_drvs = getAllStateDerivationsRecursively(root_componentPath); //TODO maybe also scan the parameters for state or component hashes? //program_args @@ -274,11 +317,13 @@ static void opRunComponent(Strings opFlags, Strings opArgs) //******************* Run **************************** - executeShellCommand(componentPath + binary + " " + program_args); //more efficient way needed ??? + executeShellCommand(root_componentPath + root_binary + " " + root_program_args); //more efficient way needed ??? //******************* With everything in place, we call the commit script on all statePaths ********************** - for (PathSet::iterator d = drvs.begin(); d != drvs.end(); ++d) + map rivisionsClosureMapping; + + for (PathSet::iterator d = root_drvs.begin(); d != root_drvs.end(); ++d) { //Extract the neccecary info from each Drv Path drvPath = *d; @@ -384,20 +429,30 @@ static void opRunComponent(Strings opFlags, Strings opArgs) p_args.push_back(nonversionedstatepathsarray); p_args.push_back(commandsarray); runProgram_AndPrintOutput(nixLibexecDir + "/nix/nix-statecommit.sh", true, p_args, "svn"); - + //TODO //Scan if needed - //scanAndUpdateAllReferencesRecusively ... - + if(false) + store->scanAndUpdateAllReferencesRecusively(statePath); + + rivisionsClosureMapping[statePath] = readRevisionNumbers(drv); //Get current numbers } - //Update all revision numbers in the database - setAllRevisionNumbers(drvs); + //Store the revision numbers in the database for this statePath to a new revision + store->setStateRevisions(root_statePath, rivisionsClosureMapping, -1); + + RevisionNumbersClosure getRivisionsClosure; + bool b = store->queryStateRevisions(root_statePath, getRivisionsClosure, -1); + + for (RevisionNumbersClosure::iterator d = getRivisionsClosure.begin(); d != getRivisionsClosure.end(); ++d){ + printMsg(lvlError, format("REVVV")); + for (RevisionNumbers::iterator e = (*d).begin(); e != (*d).end(); ++e) + printMsg(lvlError, format("REV %1%") % *e); + } } - void run(Strings args) { Strings opFlags, opArgs; @@ -519,6 +574,11 @@ void run(Strings args) op = opShowStateReposRootPath; else if (arg == "--showderivations") op = opShowDerivations; + else if (arg == "--showrevisions") + op = queryAvailableStateRevisions; + else if (arg.substr(0,21) == "--revert-to-revision=") + op = revertToRevision; + /* @@ -526,24 +586,40 @@ void run(Strings args) --run-without-commit + --showrevisions + + --revert-to-revision= + + --share-from + + --unshare + + OPTIONAL + + --scanreferences + + ///////////////////// + --backup ? --exclude-commit-paths TODO update getDerivation in nix-store to handle state indentifiers - --revert-to-state (recursive revert...) - - --delete-state - - --share-from + --delete-revision */ + else if (arg.substr(0,21) == "--revert-to-revision="){ + bool succeed = string2Int(arg.substr(21,arg.length()), revision_arg); + if(!succeed) + throw UsageError("The given revision is not a valid number"); + } else if (arg.substr(0,13) == "--identifier=") stateIdentifier = arg.substr(13,arg.length()); else if (arg.substr(0,7) == "--user=") username = arg.substr(7,arg.length()); + else opArgs.push_back(arg);