R/gittools.R

#' Reads the output from git grep into a data frame.
#' 
#' Output generated by git grep as shown below
#' $git grep -n "regex pattern" > result.txt ror you can use the function
#' provided here \code{gitGrep}
#'
#' @export
#' @param data character vector of the result so \code{$git grep ...}
#' @param filename the fully qualifed filename to read, provide this if you have 
#'    saved the data in a text file 
#' @param noText if TRUE then do not include original text in returned data frame
#' @return a data frame of the match data [,c("file", "line", "text")]
read.gitGrep <- function(data,  
   filename = NA,
   noText = FALSE){

   if (missing(data)) {
      if (is.na(filename)) stop("'data' or 'filename' must be provided")
      data <- scan(filename, what = character(0), sep = "\n", quiet= TRUE)
   }
   ix <- strsplit(data, ":", fixed = TRUE)
   ixLen <- sapply(ix, length)
   if (any(ixLen < 3)) stop("Each record in the input must be filename:line:text") 
   
   ff <- sapply(ix, function(x) x[1])
   nn <- as.numeric(sapply(ix, function(x) x[2]))
   if (noText) {
      x <- data.frame(file = ff, line = nn, stringsAsFactors = FALSE)
   } else {   
      text <- sapply(ix, function(x) paste0(x[3:length(x)], collapse = "") )
      x <- data.frame(file = ff, line = nn, text = text, stringsAsFactors = FALSE)
   }
   x
}

#' Given a data frame of gitGrep results, replace the oldString with the new
#' in each file listed
#' 
#' @export
#' @param x a data frame as read by \code{\link{read.gitGrep}}
#' @param oldString character, the string to be replaced
#' @param newString character, the new string to replace the old string
#' @param path character, read.gitGrep returns relative paths, use this argument 
#'    to specify the fully qualified filenames.
#' @return a logical vector 
gsub.gitGrep <- function(x, oldString, newString, path = "."){
   
   gsub.gitGrepOne <- function(x, oldString = "", newString = "") {
      filename <- x[1,"file"]
      line <- x[,"line"]
      #s <- scan(filename, what=character(), sep = "\n", quiet = TRUE, blank.lines.skip = FALSE, na.strings = '')
      
      ff <- file(filename, open = 'rt')
      s <- readLines(ff)
      close(ff)
      s[line] <- gsub(oldString, newString, s[line], fixed = TRUE)
      cat(s, file = filename, sep = "\n")
      file.exists(filename)
   }
   
   origPath <- setwd(path)
   xx <- split(x, x[,"file"])
   ok <- lapply(xx, gsub.gitGrepOne, oldString, newString)
   setwd(origPath)
   ok
}


#' A wrapper around the the \code{git grep -n <pattern>} command with a 
#' find-and-replace option.
#' 
#' @export
#' @param pattern regular expression to search for.  See \code{glob2rx} to convert
#'    a glob to regular expression
#' @param replacement character, if not NA then replace occurance of pattern with this
#'    string
#' @param path the path to the git snapshot
#' @param extra, character, extra modifiers for \code{git grep} that must include at least'-n'
#'    The default is "-nF" which means the pattern fixed (not regex), specify \code{extra}
#'    without "F" if you want to pass a regular expression.
#' @return a data frame as returned by \code{\link{read.gitGrep}}
gitGrep <- function(pattern, replacement = NA , path = ".", extra = "-nF"){
   
   if (system("which git") != 0) stop("Git not found by which - is it installed")
   
   ok <- grepl(".git", list.files(path = path, all.files = TRUE), fixed = TRUE)
   if ( !any(ok) ) stop(paste(".git not found in path:", path))
   
   
   origPath <- setwd(path)
   if (!grepl("n", extra)) stop("n must be part of 'extra' argument")
   
   s <- system(paste("git grep", extra,  pattern) , intern = TRUE)
   x <- read.gitGrep(s)
   if (!is.na(replacement)) ok <- gsub.gitGrep(x, pattern, replacement)
   setwd(origPath)
   x
}
BigelowLab/gittools documentation built on May 5, 2019, 2:42 p.m.