Skip to content

Instantly share code, notes, and snippets.

@rmflight
Last active March 29, 2024 19:43
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save rmflight/8863882 to your computer and use it in GitHub Desktop.
Save rmflight/8863882 to your computer and use it in GitHub Desktop.
useful commit hooks for R package dev
#!/path/2/Rscript
# License: CC0 (just be nice and point others to where you got this)
# Author: Robert M Flight <rflight79@gmail.com>, github.com/rmflight
#
# This is a post-commit hook that after a successful commit subsequently increments the package version in DESCRIPTION
# and commits that. Analogous to the pre-commit at https://gist.github.com/rmflight/8863882, but useful if you only have
# good reasons for not doing it on the pre-commit.
#
# To install it, simply copy this into the ".git/hooks/post-commit" file of your git repo, change /path/2/Rscript, and make
# it executable. Note that /path/2/Rscript is the same as your /path/2/R/bin/R, or may be in /usr/bin/Rscript depending on
# your installation. This has been tested on both Linux and Windows installations.
#
# In instances where you do NOT want the version incremented, add the environment variable doIncrement=FALSE to your git
# call. eg "doIncrement=FALSE git commit -m "commit message"".
# This is useful when you change the major version number for example.
doIncrement <- TRUE # default
# get the environment variable and modify if necessary
tmpEnv <- as.logical(Sys.getenv("doIncrement"))
if (!is.na(tmpEnv)){
doIncrement <- tmpEnv
}
if (doIncrement){
currDir <- getwd() # this should be the top level directory of the git repo
currDCF <- read.dcf("DESCRIPTION")
currVersion <- currDCF[1,"Version"]
splitVersion <- strsplit(currVersion, ".", fixed=TRUE)[[1]]
nVer <- length(splitVersion)
currEndVersion <- as.integer(splitVersion[nVer])
newEndVersion <- as.character(currEndVersion + 1)
splitVersion[nVer] <- newEndVersion
newVersion <- paste(splitVersion, collapse=".")
currDCF[1,"Version"] <- newVersion
currDCF[1, "Date"] <- strftime(as.POSIXlt(Sys.Date()), "%Y-%m-%d")
write.dcf(currDCF, "DESCRIPTION")
system("git add DESCRIPTION")
system('doIncrement=FALSE git commit -m "increment package version"') # doIncrement=FALSE is required, otherwise we end up in an infinite loop
cat("Incremented package version and committed!\n")
}
#!/path/2/Rscript
# License: CC0 (just be nice and point others to where you got this)
# Author: Robert M Flight <rflight79@gmail.com>, github.com/rmflight
#
# This is a pre-commit hook that checks that there are files to be committed, and if there are, increments the package version
# in the DESCRIPTION file.
#
# To install it, simply copy this into the ".git/hooks/pre-commit" file of your git repo, change /path/2/Rscript, and make it
# executable. Note that /path/2/Rscript is the same as your /path/2/R/bin/R, or may be in /usr/bin/Rscript depending on your
# installation. This has been tested on both Linux and Windows installations.
#
# In instances where you do NOT want the version incremented, add the environment variable doIncrement=FALSE to your git call.
# eg "doIncrement=FALSE git commit -m "commit message"".
# This is useful when you change the major version number for example.
doIncrement <- TRUE # default
# get the environment variable and modify if necessary
tmpEnv <- as.logical(Sys.getenv("doIncrement"))
if (!is.na(tmpEnv)){
doIncrement <- tmpEnv
}
# check that there are files that will be committed, don't want to increment version if there won't be a commit
fileDiff <- system("git diff HEAD --name-only", intern=TRUE)
if ((length(fileDiff) > 0) && doIncrement){
currDir <- getwd() # this should be the top level directory of the git repo
currDCF <- read.dcf("DESCRIPTION")
currVersion <- currDCF[1,"Version"]
splitVersion <- strsplit(currVersion, ".", fixed=TRUE)[[1]]
nVer <- length(splitVersion)
currEndVersion <- as.integer(splitVersion[nVer])
newEndVersion <- as.character(currEndVersion + 1)
splitVersion[nVer] <- newEndVersion
newVersion <- paste(splitVersion, collapse=".")
currDCF[1,"Version"] <- newVersion
currDCF[1, "Date"] <- strftime(as.POSIXlt(Sys.Date()), "%Y-%m-%d")
write.dcf(currDCF, "DESCRIPTION")
system("git add DESCRIPTION")
cat("Incremented package version and added to commit!\n")
}
@rmflight
Copy link
Author

rmflight commented Feb 7, 2014

As noted in the comments, these are pre-commit and post-commit files that can be used to auto-increment package version numbers. Note that the post-commit one actually results in two separate commits, the actual commit, and then one for the incremented version number.

@ajaquith
Copy link

Very cool. I also added the following line to the pre-commit version to also modify the date:

currDCF[1, "Date"] <- strftime(as.POSIXlt(Sys.Date()), "%Y-%m-%d")

@rmflight
Copy link
Author

rmflight commented Jul 7, 2014

Nice addition @ajaquith. I have added that to both entries above.

@rmflight
Copy link
Author

rmflight commented Jul 7, 2014

Another note, if you have no previous commits in your repo, and this hook is enabled, git will give you an error because there is no HEAD. Just ignore it, and the next commit it should work.

@r-cheologist
Copy link

Great! Another (nice?) addition:
currDir <- system(command = "git rev-parse --show-toplevel",intern = TRUE)
... followed by appropriate "file.path" construction of currDCF ...

@Christoph999
Copy link

Sorry, I was not aware, that there is an Rscript.exe... Now it is working :-)

@zachary-foster
Copy link

Good stuff! Thanks!

@giko45
Copy link

giko45 commented May 12, 2016

to work crossplatform use
#!/usr/bin/env Rscript
instead of
#!/path/2/Rscript
and make sure that Rscript is in you path (on all plattforms :) )

@gadenbuie
Copy link

This is awesome, thanks for sharing!

I added some logic that sets doIncrement <- FALSE if the version was changed manually. Actually, if the version was changed, then it only commits if DESCRIPTION was staged. It also writes a message if there were additional changes in DESCRIPTION that get added to the commit (although it would be easy to also bail here and not make changes to DESCRIPTION)

This is nice because you should be able manually change the version and stage DESCRIPTION all from within RStudio's version control pane without having to set the environment variable.

if ("DESCRIPTION" %in% fileDiff) {
  # Don't want to overwrite manual version bump
  desc_diff <- system("git diff HEAD DESCRIPTION", intern = TRUE)
  doIncrement <- !any(grepl("\\+Version", desc_diff))
  staged_files <- system("git diff HEAD --name-only --staged", intern = TRUE)
  desc_staged <- "DESCRIPTION" %IN% staged_files
  if (!desc_staged) {
    if (doIncrement) cat("DESCRIPTION had additional changes that were committed.\n")
    else cat("DESCRIPTION contains manual version bump but was not staged, so it was not committed.\n")
  } 
}

Also usethis::use_description() doesn't include the date field by default, so I added a check for Date before bumping that field as well:

if ("Date" %in% colnames(currDCF)) currDCF[1, "Date"] <- strftime(as.POSIXlt(Sys.Date()), "%Y-%m-%d")

@JoFAM
Copy link

JoFAM commented Mar 13, 2018

Thank you for this! In order to get this working when the package directory is not the root, I changed as follows:

if ((length(fileDiff) > 0) && doIncrement){

  DESC <- grep("DESCRIPTION",list.files(".", recursive = TRUE),value = TRUE) 
  currDCF <- read.dcf(DESC)
  currVersion <- currDCF[1,"Version"]
  splitVersion <- strsplit(currVersion, ".", fixed=TRUE)[[1]]
  nVer <- length(splitVersion)
  currEndVersion <- as.integer(splitVersion[nVer])
  newEndVersion <- as.character(currEndVersion + 1)
  splitVersion[nVer] <- newEndVersion
  newVersion <- paste(splitVersion, collapse=".")
  currDCF[1,"Version"] <- newVersion
  currDCF[1, "Date"] <- strftime(as.POSIXlt(Sys.Date()), "%Y-%m-%d")
  write.dcf(currDCF, DESC)
  system(paste("git add", DESC))
  cat("Incremented package version and added to commit!\n")
}

@aushev
Copy link

aushev commented Aug 23, 2018

(sorry maybe dumb question) - Can anyone explain what is the point of currDir <- getwd() command? I don't see currDir variable used anywhere after that

@aecoleman
Copy link

aushev: You seem to be correct that currDir isn't used anywhere. I'm thinking that it may be vestigial. Perhaps the read.dcf or write.dcf commands used to contain full pathnames, created by file.path(currDir, 'DESCRIPTION')? Or it could be that there was once a check to make sure that the current working directory was a package root? In any case, it shouldn't be necessary and can likely be safely removed.

@rmflight
Copy link
Author

I know it's been six years since the last activity on these, but I know I'm still using these regularly. I was recently motivated to improve them a bit. To make it easier to track actual changes to the hooks, I've created a proper GH repository for them here: https://github.com/rmflight/r_git_hooks

I've incorporated the idea of only changing the date if present, as well as @gadenbuie changes in their fork for not modifying the version if it's modified and staged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment