Nothing
#' Restore default library of packages, undoing all changes made by groundhog
#'
#' In a few seconds reverse changes made by groundhog to the default personal
#' library (where R packages are usually installed into with `installed.packages()`).
#' If you are just trying groundhog out for the first time,
#' or you generally rely on base R's `library()` and want to use `groundhog.library()` sporadically
#' then you may want to run `restore.library()` when you are done with your one-time use of groundhog.
#' This will undo any and all changes made by `groundhog` to that library.
#' In most circumstances, restoring a library takes a few seconds, even if the library
#' has had 100s of package modifications.
#'
#'@param days an optional numeric argument used to choose among alternative restore points.
#' When `days` is set, groundhog restores the personal library to the most recent restore point that
#' is at least `days` days old. If `dais` is not set, groundhog restores to the most recent restore point overall.
#' For example, if there are two restore points: one from today, and one from 7 days ago,
#' running `restore.library()` would restore to the former, setting `days=3` would restore to the latter,
#' and setting `days=8` would result in an error. `days = -1` restores to the oldest restore point available.
#' @examples
#' \dontrun{
#' restore.library()
#' restore.library(7)
#' restore.library(-1)
#' }
#'
#' @details
#'When groundhog installs a package, it installs it into groundhog's library
#' (location of that library is obtainable with [get.groundhog.folder()]).
#'Groundhog then immediately moves the installed package(s) (and their dependencies)
#'to the default personal library (location of that library obtainable with: `.libPaths()[1]`).
#'Altering the packages in the local folder is necessary for groundhog to work properly for two main reasons. First,
#'R Studio often loads packages from that library before users run the code in a script, creating
#'version conflicts that cannot be avoided when attempting to load other versions of those
#'packages with `groundhog`. Second, R scripts often run processes in independent R sessions, for
#'example when doing parallel processing. Those background processes will also look for
#'packages in the default personal folder. Because the personal library can only hold
#'one version of a given package, before moving new packages in, groundhog moves
#'any existing other versions of those packages out, to another directory (a local archive).
#'Those files are not deleted, just moved, making it easy and fast to recover.
#'When the first change in the personal folder is made on a given calendar date,
#'groundhog makes a list of all packages available in the personal folder before such change (saving
#'a copy of the results from `installed.packages(.libPaths()[1])`), this saved file is referred to as a
#'a 'restore point'. With `restore.library()` groundhog looks up a restore point, obtain the set of packages
#'that used to be installed, and removes any packages *installed by groundhog* which are in the personal library
#'but were not in that restore point; similarly, it moves back to the local library any packages *removed by groundhog*
#'that were in the restore point but are not currently there. This process take a few seconds even for 100+ packages.
#'Note that there is only one restore point per calendar date, so one effectively restores
#'the personal library to how it was before *any* changes were made to it that day with groundhog.
#'Restore points are saved permanently and can be restored at any point.
#'The set of restore points available is stored in the hidden dataframe `.available.restore.points`.
#'To choose among them use the `days` argument in `restore.library`. The default is to restore
#'based on the most recent restore point, so if a user installs groundhog, tests it, and wants to
#'undo all changes made by groundhog, the default behavior will achieve this goal.
#'Note: restoring can take a few minutes if the groundhog folder is on a different
#'drive from the default personal R library (e.g., two different hard drives) or if it is in Dropbox.
#' @export
#'
restore.library<-function(days=0)
{
#1 Current IP
ip.local <- get.ip('local')
ip.backup <- get.ip('backup')
ip.groundhog <-get.ip('groundhog')
loans <- get.loans()
#2 Chose restore point to use
#2.1 Set the path
restore_dir <- paste0(get.groundhog.folder(),"/restore_points/", get.r.majmin())
dir.create(restore_dir, recursive = TRUE,showWarnings = FALSE)
#2.3 Files available
ip_files <- list.files(restore_dir)
#if none, end
if (length(ip_files)==0) {
message1("No restore points are available.")
return(invisible(FALSE))
}
#2.4 Turn filenames to dates
ip_dates <- as.Date(substr(ip_files, 0,10))
#2.5 Set date for restore based on days
days.since <- Sys.Date() - ip_dates
ip_dates <- ip_dates[days.since >= days]
if (length(ip_dates)==0) {
message("The oldest restore point is from ",max(days.since)," days ago.")
return(invisible(FALSE))
}
datek <- max(ip_dates)
#3 Read restore IP for datek, all packages in install.package for restore point
ip.restore <- readRDS(paste0(restore_dir, "/", datek, ".rds"))
#---------------------------------------------------------------------------
#ADD PACAKGES THAT WERE REMOVED BY GROUNDHOG
#4 Subset of packages in to-be-restored IP, but not in local, so they need to be added
ip.add0 <- ip.restore[! ip.restore$md5 %in% ip.local$md5,] #We used to have these packages, but no longer do, so we add them
#4.1 Keep subset that we know were originally removed by groundhog
ip.add <- ip.add0[ip.add0$md5 %in% ip.backup$md5,] #only keep those that were removed by groundhog
#4.2 If some were removed not by groundhog make a note
non.groundhog_removed <- ip.add0$pkg_vrs[!ip.add0$pkg_vrs %in% ip.add$pkg_vrs]
#4.3 Count how many are being added
n.add <- nrow(ip.add)
#----------------------------------------------------
#5 Set of pkgs being eliminated
ip.purge0 <- ip.local[!ip.local$md5 %in% ip.restore$md5,] #We have these packages but did not use to, so we purge them
#5.1 Do eliminate those we know were originally added by groundhog
ip.purge <- ip.purge0[ip.purge0$md5 %in% loans$md5 | ip.purge0$md5 %in% ip.groundhog$md5,]
#We have these packages but did not use to, so we purge them
#They are either on loan (if using renaming, or in groundhog, if using copying)
#5.2 i some were added not by groundhog make a note
non.groundhog_installed <- ip.purge0$pkg_vrs[!ip.purge0$pkg_vrs %in% ip.purge$pkg_vrs]
#5.3 count number being removed
n.purge <- nrow(ip.purge)
#6 Early return if no changes
if (n.add + n.purge == 0) {
message1("No packages added or removed by groundhog since restore point.")
return(invisible(TRUE))
}
#7 Inform user what would happened if they go forward
#7.1 Package counts
message1("Proceeding entails restoring ", n.add, " packages and uninstalling ", n.purge ," packages.\n",
"This process should take a few seconds.\n")
#7.2 Warnings for non-groundhog changes
#Commenting out for V3.1.0 - seems difficult for users to use this information really
#and not clear this poses additional risk to just switching groundhog<->library
# n1=length(non.groundhog_installed)
# n2=length(non.groundhog_removed)
# if (n1+n2>0) {
# msg <- paste0("-------------------------------------------------------------\n",
# "Warning: Since the creation of the restore point,\n")
# if (n1>0) {
# msg<-paste0(msg, "an additional ", n1 , " packages have been installed but not\n",
# "by groundhog, these packages will *not* be removed:\n",
# pasteN(non.groundhog_installed),"\n")
# }
# if (n2>0) {
# msg<-paste0(msg, "an additional ", n2 , " packages have been removed but not\n",
# "by groundhog, these will *not* be restored:\n",
# pasteN(non.groundhog_removed))
#
# }
# msg<-paste0(msg, ".\nProceeding with restoring only the groundhog produced\n",
# "changes to the local library may lead packages to not\n",
# "work properly.\n",
# "See 'https://groundhogR.com/restore' for more information.\n",
# "-------------------------------------------------------------")
# message(msg)
# }
#7.3. Prompt for 'restore'
message1("To proceed type 'restore', to stop type anything else.")
answer <-readline(prompt=" >")
if (tolower(answer) != 'restore') {
message("You typed '",answer,"', the library was NOT restored.")
return(invisible(FALSE))
}
#-----------------------------
#8 If user says "RESTORE"
if (tolower(answer) == 'restore') {
#8.1 Purge packages (back to groundhog or put to backup)
purge.local (ip.purge , loans) #function 1 in interlibrary.functions.R
#This sends back to groundhog pkgs that came from there (based on MD5)
#and sends to backup those that do no match md5
#8.2 Restore deleted packages #k=1
skip.no_backup=skip.other_pkg=c()
if (n.add > 0)
{
backup.dir <- paste0(get.groundhog.folder(),"/restore_library/",get.r.majmin(),"/")
dir.create(backup.dir, showWarnings = FALSE, recursive = TRUE)
#From/To based on ip.add (recall: ip.add is a subset of ip.restore)
from <-paste0(backup.dir, ip.add$pkg_vrs,"/", ip.add$Package)
to <- paste0(ip.add$LibPath, "/", get.pkg(ip.add$pkg))
#Skip if from does not exist skip
skip.no_backup <- !file.exists(from)
skip.other_pkg <- file.exists(paste0(to,"/", ip.add$pkg))
skip <- skip.no_backup | skip.other_pkg
file.rename.robust2(from,to) #see file.rename.robust2.R
#Delete parent directory
#(so, from = 'pkg_vrs'/'pkg'
unlink(dirname(from),recursive=TRUE)
}
#------------------------------------------------
#Skipped based on FROM
if (sum(skip.no_backup)>0) {
message(sum(skip.no_backup), " packages originally removed by groundhog could not be restored because the backup was missing:")
message(pasteN(from[skip.no_backup]))
}
#Skipped based on TO
if (sum(skip.other_pkg)>0) {
message(sum(skip.other_pkg), " packages originally removed by groundhog could not be restored because another version of the same \n",
"package was installed, not with groundhog, after the restore point was created:")
message(pasteN(from[skip.other_pkg]))
}
#9 Compare obtained with expected
ip.now<-get.ip('local')
n.purge.success <- sum(!ip.purge$md5 %in% ip.now$md5)
n.add.success <- sum(ip.add$md5 %in% ip.now$md5)
#10 Message
msg = paste0("Restore library results:\n",
" - ", n.purge.success, " out of ", n.purge, " intended packages were successfully removed\n",
" - ", n.add.success," out of ", n.add, " intended packages were successfully restored")
prompt.msg = "\nRestart the R session to finalize the restore process."
#Verify
message1(msg)
infinite.prompt(prompt.msg , valid_answers='uncle' , must.restart = TRUE)
} #IF they answered 'restore
} #End of `restore` function
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.