diff --git a/.Rbuildignore b/.Rbuildignore index ed485bb90..f6acda6e3 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -16,3 +16,4 @@ ^\.Rproj\.user$ ^windows ^.*\.a$ +^man-roxygen$ diff --git a/DESCRIPTION b/DESCRIPTION index f98227418..aed352fec 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -12,12 +12,15 @@ BugReports: https://github.com/ropensci/git2r/issues Maintainer: Stefan Widgren Author: See AUTHORS file. Imports: + assertthat, graphics, utils Depends: R (>= 3.1), methods Suggests: + dplyr, + testthat, getPass Type: Package LazyData: true @@ -26,7 +29,7 @@ NeedsCompilation: yes SystemRequirements: zlib headers and library. OpenSSL headers and library. LibSSH2 (optional on non-Windows) to enable the SSH transport. -Collate: +Collate: 'S4_classes.r' 'blame.r' 'blob.r' @@ -40,8 +43,13 @@ Collate: 'diff.r' 'fetch.r' 'git2r.r' + 'git_connection.r' + 'git_recent.r' + 'git_sha.r' 'index.r' + 'is_git.R' 'libgit2.r' + 'list_files_git.r' 'merge.r' 'note.r' 'odb.r' @@ -50,9 +58,11 @@ Collate: 'punch_card.r' 'refspec.r' 'push.r' + 'read_delim_git.r' 'reference.r' 'reflog.r' 'remote.r' + 'remove_files_git.r' 'repository.r' 'reset.r' 'revparse.r' @@ -63,4 +73,5 @@ Collate: 'time.r' 'tree.r' 'when.r' + 'write_delim_git.r' RoxygenNote: 6.0.1 diff --git a/NAMESPACE b/NAMESPACE index 725dadac2..e07988f0c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,6 +6,8 @@ S3method(print,git_status) export(config) export(cred_ssh_key) export(cred_token) +export(git_connection) +export(is.git) export(is_blob) export(is_branch) export(is_commit) @@ -24,6 +26,7 @@ exportClasses(git_blame_hunk) exportClasses(git_blob) exportClasses(git_branch) exportClasses(git_commit) +exportClasses(git_connection) exportClasses(git_diff) exportClasses(git_diff_file) exportClasses(git_diff_hunk) @@ -69,6 +72,8 @@ exportMethods(diff) exportMethods(discover_repository) exportMethods(fetch) exportMethods(fetch_heads) +exportMethods(git_recent) +exportMethods(git_sha) exportMethods(hash) exportMethods(hashfile) exportMethods(head) @@ -84,6 +89,7 @@ exportMethods(is_local) exportMethods(is_merge) exportMethods(is_shallow) exportMethods(length) +exportMethods(list_files_git) exportMethods(lookup) exportMethods(merge) exportMethods(merge_base) @@ -98,6 +104,7 @@ exportMethods(plot) exportMethods(pull) exportMethods(punch_card) exportMethods(push) +exportMethods(read_delim_git) exportMethods(references) exportMethods(reflog) exportMethods(remote_add) @@ -107,6 +114,7 @@ exportMethods(remote_rename) exportMethods(remote_set_url) exportMethods(remote_url) exportMethods(remotes) +exportMethods(remove_files_git) exportMethods(repository) exportMethods(reset) exportMethods(revparse_single) @@ -123,13 +131,29 @@ exportMethods(tags) exportMethods(tree) exportMethods(when) exportMethods(workdir) +exportMethods(write_delim_git) import(methods) +importFrom(assertthat,assert_that) +importFrom(assertthat,has_name) +importFrom(assertthat,is.flag) +importFrom(assertthat,is.string) +importFrom(assertthat,noNA) importFrom(graphics,axis) importFrom(graphics,barplot) importFrom(graphics,par) importFrom(graphics,plot.new) importFrom(graphics,plot.window) importFrom(graphics,symbols) +importFrom(methods,new) +importFrom(methods,setClass) +importFrom(methods,setClassUnion) +importFrom(methods,setGeneric) +importFrom(methods,setMethod) +importFrom(methods,setValidity) importFrom(utils,capture.output) +importFrom(utils,file_test) +importFrom(utils,read.delim) +importFrom(utils,read.table) importFrom(utils,sessionInfo) +importFrom(utils,write.table) useDynLib(git2r, .registration=TRUE) diff --git a/R/S4_classes.r b/R/S4_classes.r index 97209e153..701fa4e7e 100644 --- a/R/S4_classes.r +++ b/R/S4_classes.r @@ -840,3 +840,54 @@ setClass("git_merge_result", conflicts = "logical", sha = "character") ) + +#' @importFrom methods setClassUnion +setClassUnion("git_credentials", c("NULL", "cred_user_pass", "cred_ssh_key")) + +#' The git_connection class +#' +#' @section Slots: +#' \describe{ +#' \item{\code{Repository}}{a git repository} +#' \item{\code{LocalPath}}{a subdirectory wihtin the repository} +#' \item{\code{Credentials}}{the credentials for the repository} +#' \item{\code{CommitUser}}{the name of the user how will commit} +#' \item{\code{CommitEmail}}{the email of the user how will commit} +#' } +#' @name git_connection-class +#' @rdname git_connection-class +#' @exportClass git_connection +#' @aliases git_connection-class +#' @importFrom methods setClass +#' @docType class +#' @template thierry +setClass( + Class = "git_connection", + representation = representation( + Repository = "git_repository", + LocalPath = "character", + Credentials = "git_credentials", + CommitUser = "character", + CommitEmail = "character" + ) +) + +#' @importFrom methods setValidity +#' @importFrom assertthat assert_that is.string has_name +#' @importFrom utils file_test +setValidity( + "git_connection", + function(object){ + assert_that(is.string(object@CommitUser)) + assert_that(is.string(object@CommitEmail)) + root <- normalizePath(object@Repository@path) + normalizePath(paste(root, object@LocalPath, sep = "/")) + repo <- repository(root) + repo.config <- config(repo) + assert_that(has_name(repo.config$local, "user.name")) + assert_that(has_name(repo.config$local, "user.email")) + assert_that(repo.config$local$user.name == object@CommitUser) + assert_that(repo.config$local$user.email == object@CommitEmail) + return(TRUE) + } +) diff --git a/R/commit.r b/R/commit.r index 823a3dd33..06a9af4a3 100644 --- a/R/commit.r +++ b/R/commit.r @@ -172,6 +172,23 @@ setMethod("commit", } ) +##' @rdname commit-methods +##' @export +##' @include S4_classes.r +setMethod("commit", + signature(repo = "git_connection"), + function(repo, message, all, session, reference, author, committer) { + commit( + repo = repo@Repository, + message = message, + all = all, + session = session, + reference = reference, + author = author, + committer = committer + ) + } +) ##' Commits ##' ##' @rdname commits-methods diff --git a/R/git_connection.r b/R/git_connection.r new file mode 100644 index 000000000..1cf8e29e0 --- /dev/null +++ b/R/git_connection.r @@ -0,0 +1,109 @@ +#' Open a git connection +#' @name git_connection +#' @rdname git_connection-class +#' @param repo.path The path of the root of the repository +#' @param local.path A path within the repository +#' @param key Optional: the path to a private ssh key. The public key is assumed +#' to have the same path with a '.pub' extension. Using in case of ssh +#' authentication. +#' @param username The optional username used in case of https authentication. +#' Ignored when \code{key} is provided. +#' @param password The password required for the ssh key or the username. Should +#' be missing when the ssh-key doesn't require a password. +#' @param commit.user the name of the user how will commit +#' @param commit.email the email of the user how will commit +#' @export +#' @importFrom methods new +#' @importFrom assertthat assert_that is.string +#' @include S4_classes.r +#' @template thierry +git_connection <- function( + repo.path, + local.path = ".", + key, + username, + password, + commit.user, + commit.email +){ + assert_that(is.string(local.path)) + assert_that(is.string(commit.user)) + assert_that(is.string(commit.email)) + assert_that( + is.git(path = repo.path), + msg = paste(repo.path, "is not a git repo") + ) + repo <- repository(repo.path) + config(repo, user.name = commit.user, user.email = commit.email) + assert_that( + dir.exists(paste(repo.path, local.path, sep = "/")), + msg = paste(local.path, "is not a directory") + ) + + if (missing(key) & missing(username)) { + return( + new( + "git_connection", + Repository = repo, + LocalPath = local.path, + Credentials = NULL, + CommitUser = commit.user, + CommitEmail = commit.email + ) + ) + } + + if (missing(username)) { + assert_that(is.string(key)) + + if (missing(password)) { + return( + new( + "git_connection", + Repository = repo, + LocalPath = local.path, + Credentials = cred_ssh_key( + publickey = paste0(key, ".pub"), + privatekey = key + ), + CommitUser = commit.user, + CommitEmail = commit.email + ) + ) + } + + assert_that(is.string(password)) + return( + new( + "git_connection", + Repository = repo, + LocalPath = local.path, + Credentials = cred_ssh_key( + publickey = paste0(key, ".pub"), + privatekey = key, + passphrase = password + ), + CommitUser = commit.user, + CommitEmail = commit.email + ) + ) + } + + assert_that(is.string(username)) + assert_that(username != "") + assert_that(is.string(password)) + assert_that(password != "") + return( + new( + "git_connection", + Repository = repo, + LocalPath = local.path, + Credentials = cred_user_pass( + username = username, + password = password + ), + CommitUser = commit.user, + CommitEmail = commit.email + ) + ) +} diff --git a/R/git_recent.r b/R/git_recent.r new file mode 100644 index 000000000..ca6cb4ea9 --- /dev/null +++ b/R/git_recent.r @@ -0,0 +1,65 @@ +#' Get the info from the latest commit of a file +#' @inheritParams read_delim_git +#' @name git_recent +#' @rdname git_recent +#' @exportMethod git_recent +#' @docType methods +#' @importFrom methods setGeneric +#' @include git_connection.r +#' @template thierry +setGeneric( + name = "git_recent", + def = function(file, connection, ...){ + standard.generic("git_recent") + } +) + +#' @rdname git_recent +#' @aliases git_recent,git_connection-methods +#' @importFrom methods setMethod +setMethod( + f = "git_recent", + signature = signature(connection = "ANY"), + definition = function(file, connection, ...){ + this.connection <- git_connection(repo.path = connection, ...) + git_recent(file = file, connection = this.connection) + } +) + +#' @rdname git_recent +#' @aliases git_recent,git_connection-methods +#' @importFrom methods setMethod +#' @importFrom assertthat assert_that is.string +setMethod( + f = "git_recent", + signature = signature(connection = "git_connection"), + definition = function(file, connection, ...){ + assert_that(is.string(file)) + + if (is.null(head(connection@Repository))) { + stop("no commits in current branch") + } + + old.wd <- getwd() + setwd(connection@Repository@path) + commit.info <- system( + paste0( + "git log -n 1 --date=iso ", + connection@LocalPath, "/", file + ), + intern = TRUE + ) + setwd(old.wd) + date <- commit.info[grep("^Date:", commit.info)] + date <- as.POSIXct(date, format = "Date: %F %T %z") + commit <- commit.info[grep("^commit", commit.info)] + commit <- gsub("^commit ", "", commit) + author <- commit.info[grep("^Author:", commit.info)] + author <- gsub("^Author: ", "", author) + return(list( + Commit = commit, + Author = author, + Date = date + )) + } +) diff --git a/R/git_sha.r b/R/git_sha.r new file mode 100644 index 000000000..0722d88e9 --- /dev/null +++ b/R/git_sha.r @@ -0,0 +1,70 @@ +#' Get the SHA of the files at the HEAD +#' @inheritParams read_delim_git +#' @name git_sha +#' @rdname git_sha +#' @exportMethod git_sha +#' @docType methods +#' @importFrom methods setGeneric +#' @include git_connection.r +#' @template thierry +setGeneric( + name = "git_sha", + def = function(file, connection, ...){ + standard.generic("git_sha") + } +) + +#' @rdname git_sha +#' @aliases git_sha,git_connection-methods +#' @importFrom methods setMethod +setMethod( + f = "git_sha", + signature = signature(connection = "ANY"), + definition = function(file, connection, ...){ + this.connection <- git_connection(repo.path = connection, ...) + git_sha(file = file, connection = this.connection) + } +) + +#' @rdname git_sha +#' @aliases git_sha,git_connection-methods +#' @importFrom methods setMethod +#' @importFrom assertthat assert_that noNA +#' @importFrom utils read.table +setMethod( + f = "git_sha", + signature = signature(connection = "git_connection"), + definition = function(file, connection, ...){ + assert_that(is.character(file)) + assert_that(noNA(file)) + + if (is.null(head(connection@Repository))) { + stop("no commits available") + } + + old.wd <- getwd() + setwd(connection@Repository@path) + blobs <- system( + paste( + "git ls-tree -r HEAD", + connection@LocalPath + ), + ignore.stderr = TRUE, + intern = TRUE + ) + setwd(old.wd) + blobs <- read.table( + textConnection(paste(blobs, collapse = "\n")), + header = FALSE, + sep = "\t", + stringsAsFactors = FALSE, + col.names = c("SHA", "Path") + ) + blobs$File <- basename(blobs$Path) + blobs <- blobs[blobs$File %in% file, ] + blobs$Path <- dirname(blobs$Path) + blobs$SHA <- gsub("^.*blob ", "", blobs$SHA) + + return(blobs) + } +) diff --git a/R/is_git.R b/R/is_git.R new file mode 100644 index 000000000..de9579aaf --- /dev/null +++ b/R/is_git.R @@ -0,0 +1,10 @@ +#' Is the path a git repository +#' Checks if a '.git' subdirectory exists in 'path' +#' @param path the path to check +#' @export +#' @return A logical vector with the same length as path +#' @importFrom utils file_test +#' @template thierry +is.git <- function(path){ + file_test("-d", paste(path, ".git", sep = "/")) +} diff --git a/R/list_files_git.r b/R/list_files_git.r new file mode 100644 index 000000000..b637efd93 --- /dev/null +++ b/R/list_files_git.r @@ -0,0 +1,55 @@ +#' List the files in a path of a git repository +#' @inheritParams read_delim_git +#' @inheritParams base::list.files +#' @name list_files_git +#' @rdname list_files_git +#' @exportMethod list_files_git +#' @docType methods +#' @importFrom methods setGeneric +#' @include git_connection.r +#' @template thierry +setGeneric( + name = "list_files_git", + def = function(connection, pattern = NULL, full.names = FALSE, ...){ + standard.generic("list_files_git") + } +) + +#' @rdname list_files_git +#' @aliases list_files_git,git_connection-methods +#' @importFrom methods setMethod +setMethod( + f = "list_files_git", + signature = signature(connection = "ANY"), + definition = function(connection, pattern = NULL, full.names = FALSE, ...){ + this.connection <- git_connection(repo.path = connection, ...) + list_files_git( + connection = this.connection, + pattern = pattern, + full.names = full.names + ) + } +) + +#' @rdname list_files_git +#' @aliases list_files_git,git_connection-methods +#' @importFrom methods setMethod +#' @importFrom assertthat assert_that is.string is.flag noNA +setMethod( + f = "list_files_git", + signature = signature(connection = "git_connection"), + definition = function(connection, pattern = NULL, full.names = FALSE, ...){ + if (!is.null(pattern)) { + assert_that(is.string(pattern)) + } + assert_that(is.flag(full.names)) + assert_that(noNA(full.names)) + + full.path <- sprintf( + "%s/%s", + connection@Repository@path, + connection@LocalPath + ) + list.files(path = full.path, pattern = pattern, full.names = full.names) + } +) diff --git a/R/read_delim_git.r b/R/read_delim_git.r new file mode 100644 index 000000000..89b16f9f9 --- /dev/null +++ b/R/read_delim_git.r @@ -0,0 +1,55 @@ +#' Read a tab delimited file from a git repository +#' @param file the name of the file +#' @param connection The path of a git repository or a \code{git_connection} +#' object +#' @param ... parameters passed to \code{git_connection()} when +#' \code{connection} is a path +#' @name read_delim_git +#' @rdname read_delim_git +#' @exportMethod read_delim_git +#' @docType methods +#' @importFrom methods setGeneric +#' @include git_connection.r +#' @template thierry +setGeneric( + name = "read_delim_git", + def = function(file, connection, ...){ + standard.generic("read_delim_git") + } +) + +#' @rdname read_delim_git +#' @aliases read_delim_git,git_connection-methods +#' @importFrom methods setMethod +setMethod( + f = "read_delim_git", + signature = signature(connection = "ANY"), + definition = function(file, connection, ...){ + this.connection <- git_connection(repo.path = connection, ...) + read_delim_git(file = file, connection = this.connection) + } +) + +#' @rdname read_delim_git +#' @aliases read_delim_git,git_connection-methods +#' @importFrom methods setMethod +#' @importFrom utils read.delim +#' @importFrom assertthat assert_that is.string +setMethod( + f = "read_delim_git", + signature = signature(connection = "git_connection"), + definition = function(file, connection, ...){ + assert_that(is.string(file)) + + filename <- normalizePath( + sprintf( + "%s/%s/%s", + connection@Repository@path, + connection@LocalPath, + file + ), + mustWork = TRUE + ) + return(read.delim(filename, stringsAsFactors = FALSE)) + } +) diff --git a/R/remove_files_git.r b/R/remove_files_git.r new file mode 100644 index 000000000..47901309b --- /dev/null +++ b/R/remove_files_git.r @@ -0,0 +1,66 @@ +#' Remove all the files in a path of a git repository +#' +#' @inheritParams read_delim_git +#' @inheritParams base::list.files +#' @name remove_files_git +#' @rdname remove_files_git +#' @exportMethod remove_files_git +#' @docType methods +#' @importFrom methods setGeneric +#' @include list_files_git.r +#' @template thierry +setGeneric( + name = "remove_files_git", + def = function(connection, pattern = NULL, ...){ + standard.generic("remove_files_git") + } +) + +#' @rdname remove_files_git +#' @aliases remove_files_git,git_connection-methods +#' @importFrom methods setMethod +setMethod( + f = "remove_files_git", + signature = signature(connection = "ANY"), + definition = function(connection, pattern = NULL, ...){ + this.connection <- git_connection( + repo.path = connection, + ... + ) + remove_files_git(connection = this.connection, pattern = pattern) + } +) + +#' @rdname remove_files_git +#' @aliases remove_files_git,git_connection-methods +#' @importFrom methods setMethod +setMethod( + f = "remove_files_git", + signature = signature(connection = "git_connection"), + definition = function(connection, pattern = NULL, ...){ + to.remove <- list_files_git( + connection = connection, + pattern = pattern, + full.names = TRUE + ) + + success <- suppressWarnings(file.remove(to.remove)) + + if (length(success) > 0 && !all(success)) { + stop( + "Error cleaning existing files in the git repository. Repository: '", + connection@Repository@path, "', Path: '", connection@LocalPath, + "', pattern: '", pattern, "'" + ) + } + if (any(success)) { + to.stage <- gsub( + paste0("^", connection@Repository@path, "/"), + "", + to.remove[success] + ) + add(repo = connection@Repository, path = to.stage) + } + return(invisible(TRUE)) + } +) diff --git a/R/repository.r b/R/repository.r index 5123882b5..7a5de4700 100644 --- a/R/repository.r +++ b/R/repository.r @@ -785,6 +785,17 @@ setMethod("default_signature", } ) +##' @rdname default_signature-methods +##' @export +##' @include S4_classes.r +setMethod("default_signature", + signature(repo = "git_connection"), + function(repo) + { + .Call(git2r_signature_default, repo@Repository) + } +) + ##' Brief summary of repository ##' ##' @aliases show,git_repository-methods diff --git a/R/write_delim_git.r b/R/write_delim_git.r new file mode 100644 index 000000000..5f9cf2db9 --- /dev/null +++ b/R/write_delim_git.r @@ -0,0 +1,84 @@ +#' Write a dataframe as a tab delimited file to a git repository and stage it +#' +#' The existing file will be overwritten. +#' @param x the data.frame +#' @param ... parameters passed to \code{git_connection()} when +#' \code{connection} is a path. When \code{force} is available it is passed +#' to \code{add()}. +#' @return the SHA1 of the file +#' @inheritParams read_delim_git +#' @name write_delim_git +#' @rdname write_delim_git +#' @exportMethod write_delim_git +#' @docType methods +#' @importFrom methods setGeneric +#' @importFrom utils write.table +#' @include git_connection.r +#' @template thierry +setGeneric( + name = "write_delim_git", + def = function(x, file, connection, ...){ + standard.generic("write_delim_git") + } +) + +#' @rdname write_delim_git +#' @aliases write_delim_git,git_connection-methods +#' @importFrom methods setMethod +setMethod( + f = "write_delim_git", + signature = signature(connection = "ANY"), + definition = function(x, file, connection, ...){ + this.connection <- git_connection(repo.path = connection, ...) + write_delim_git(x = x, file = file, connection = this.connection) + } +) + +#' @rdname write_delim_git +#' @aliases write_delim_git,git_connection-methods +#' @importFrom methods setMethod +#' @importFrom assertthat assert_that is.string has_name +setMethod( + f = "write_delim_git", + signature = signature(connection = "git_connection"), + definition = function(x, file, connection, ...){ + assert_that(inherits(x, "data.frame")) + assert_that(is.string(file)) + + # write the file + filename.full <- paste( + connection@Repository@path, + connection@LocalPath, + file, + sep = "/" + ) + filename.full <- normalizePath( + path = filename.full, + winslash = "/", + mustWork = FALSE + ) + write.table( + x = x, file = filename.full, append = FALSE, + quote = FALSE, sep = "\t", row.names = FALSE, fileEncoding = "UTF-8" + ) + + # stage the file + if (connection@LocalPath == ".") { + filename.local <- file + } else { + filename.local <- paste(connection@LocalPath, file, sep = "/") + } + dots <- list(...) + if (has_name(dots, "force")) { + add( + repo = connection@Repository, + path = filename.local, + force = dots$force + ) + } else { + add(repo = connection@Repository, path = filename.local) + } + + return(hashfile(filename.full)) + } +) diff --git a/inst/AUTHORS b/inst/AUTHORS index 5f9710344..d4b9db5b6 100644 --- a/inst/AUTHORS +++ b/inst/AUTHORS @@ -14,6 +14,7 @@ Jim Hester List references in a remote repository Karthik Ram Summarize contributions to a repository Various fixes of code and documentation Stefan Widgren Most of the git2r bindings to libgit2 +Thierry Onkelinx Functions to work with git data repositories Other contributors to git2r diff --git a/man-roxygen/thierry.r b/man-roxygen/thierry.r new file mode 100644 index 000000000..6fee92567 --- /dev/null +++ b/man-roxygen/thierry.r @@ -0,0 +1 @@ +#' @author Thierry Onkelinx, \email{thierry.onkelinx@inbo.be} diff --git a/man/commit-methods.Rd b/man/commit-methods.Rd index 593c06384..d77ff8a53 100644 --- a/man/commit-methods.Rd +++ b/man/commit-methods.Rd @@ -4,6 +4,7 @@ \name{commit} \alias{commit} \alias{commit,git_repository-method} +\alias{commit,git_connection-method} \title{Commit} \usage{ commit(repo, message = NULL, all = FALSE, session = FALSE, @@ -13,6 +14,10 @@ commit(repo, message = NULL, all = FALSE, session = FALSE, \S4method{commit}{git_repository}(repo, message = NULL, all = FALSE, session = FALSE, reference = "HEAD", author = default_signature(repo), committer = default_signature(repo)) + +\S4method{commit}{git_connection}(repo, message = NULL, all = FALSE, + session = FALSE, reference = "HEAD", author = default_signature(repo), + committer = default_signature(repo)) } \arguments{ \item{repo}{The repository \code{object}.} diff --git a/man/default_signature-methods.Rd b/man/default_signature-methods.Rd index 25af98a69..f66f03cb2 100644 --- a/man/default_signature-methods.Rd +++ b/man/default_signature-methods.Rd @@ -4,11 +4,14 @@ \name{default_signature} \alias{default_signature} \alias{default_signature,git_repository-method} +\alias{default_signature,git_connection-method} \title{Get the signature} \usage{ default_signature(repo) \S4method{default_signature}{git_repository}(repo) + +\S4method{default_signature}{git_connection}(repo) } \arguments{ \item{repo}{The repository \code{object} to check signature} diff --git a/man/git_connection-class.Rd b/man/git_connection-class.Rd new file mode 100644 index 000000000..1bd15e559 --- /dev/null +++ b/man/git_connection-class.Rd @@ -0,0 +1,51 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/S4_classes.r, R/git_connection.r +\docType{class} +\name{git_connection-class} +\alias{git_connection-class} +\alias{git_connection} +\title{The git_connection class} +\usage{ +git_connection(repo.path, local.path = ".", key, username, password, + commit.user, commit.email) +} +\arguments{ +\item{repo.path}{The path of the root of the repository} + +\item{local.path}{A path within the repository} + +\item{key}{Optional: the path to a private ssh key. The public key is assumed +to have the same path with a '.pub' extension. Using in case of ssh +authentication.} + +\item{username}{The optional username used in case of https authentication. +Ignored when \code{key} is provided.} + +\item{password}{The password required for the ssh key or the username. Should +be missing when the ssh-key doesn't require a password.} + +\item{commit.user}{the name of the user how will commit} + +\item{commit.email}{the email of the user how will commit} +} +\description{ +The git_connection class + +Open a git connection +} +\section{Slots}{ + + \describe{ + \item{\code{Repository}}{a git repository} + \item{\code{LocalPath}}{a subdirectory wihtin the repository} + \item{\code{Credentials}}{the credentials for the repository} + \item{\code{CommitUser}}{the name of the user how will commit} + \item{\code{CommitEmail}}{the email of the user how will commit} + } +} + +\author{ +Thierry Onkelinx, \email{thierry.onkelinx@inbo.be} + +Thierry Onkelinx, \email{thierry.onkelinx@inbo.be} +} diff --git a/man/git_recent.Rd b/man/git_recent.Rd new file mode 100644 index 000000000..7afd845de --- /dev/null +++ b/man/git_recent.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/git_recent.r +\docType{methods} +\name{git_recent} +\alias{git_recent} +\alias{git_recent,ANY,ANY-method} +\alias{git_recent,git_connection-methods} +\alias{git_recent,ANY,git_connection-method} +\alias{git_recent,git_connection-methods} +\title{Get the info from the latest commit of a file} +\usage{ +git_recent(file, connection, ...) + +\S4method{git_recent}{ANY,ANY}(file, connection, ...) + +\S4method{git_recent}{ANY,git_connection}(file, connection, ...) +} +\arguments{ +\item{file}{the name of the file} + +\item{connection}{The path of a git repository or a \code{git_connection} +object} + +\item{...}{parameters passed to \code{git_connection()} when +\code{connection} is a path} +} +\description{ +Get the info from the latest commit of a file +} +\author{ +Thierry Onkelinx, \email{thierry.onkelinx@inbo.be} +} diff --git a/man/git_sha.Rd b/man/git_sha.Rd new file mode 100644 index 000000000..41e0d1cc9 --- /dev/null +++ b/man/git_sha.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/git_sha.r +\docType{methods} +\name{git_sha} +\alias{git_sha} +\alias{git_sha,ANY,ANY-method} +\alias{git_sha,git_connection-methods} +\alias{git_sha,ANY,git_connection-method} +\alias{git_sha,git_connection-methods} +\title{Get the SHA of the files at the HEAD} +\usage{ +git_sha(file, connection, ...) + +\S4method{git_sha}{ANY,ANY}(file, connection, ...) + +\S4method{git_sha}{ANY,git_connection}(file, connection, ...) +} +\arguments{ +\item{file}{the name of the file} + +\item{connection}{The path of a git repository or a \code{git_connection} +object} + +\item{...}{parameters passed to \code{git_connection()} when +\code{connection} is a path} +} +\description{ +Get the SHA of the files at the HEAD +} +\author{ +Thierry Onkelinx, \email{thierry.onkelinx@inbo.be} +} diff --git a/man/is.git.Rd b/man/is.git.Rd new file mode 100644 index 000000000..05bb3272f --- /dev/null +++ b/man/is.git.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/is_git.R +\name{is.git} +\alias{is.git} +\title{Is the path a git repository +Checks if a '.git' subdirectory exists in 'path'} +\usage{ +is.git(path) +} +\arguments{ +\item{path}{the path to check} +} +\value{ +A logical vector with the same length as path +} +\description{ +Is the path a git repository +Checks if a '.git' subdirectory exists in 'path' +} +\author{ +Thierry Onkelinx, \email{thierry.onkelinx@inbo.be} +} diff --git a/man/list_files_git.Rd b/man/list_files_git.Rd new file mode 100644 index 000000000..33899b36b --- /dev/null +++ b/man/list_files_git.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/list_files_git.r +\docType{methods} +\name{list_files_git} +\alias{list_files_git} +\alias{list_files_git,ANY-method} +\alias{list_files_git,git_connection-methods} +\alias{list_files_git,git_connection-method} +\alias{list_files_git,git_connection-methods} +\title{List the files in a path of a git repository} +\usage{ +list_files_git(connection, pattern = NULL, full.names = FALSE, ...) + +\S4method{list_files_git}{ANY}(connection, pattern = NULL, + full.names = FALSE, ...) + +\S4method{list_files_git}{git_connection}(connection, pattern = NULL, + full.names = FALSE, ...) +} +\arguments{ +\item{connection}{The path of a git repository or a \code{git_connection} +object} + +\item{pattern}{an optional \link{regular expression}. Only file names + which match the regular expression will be returned.} + +\item{full.names}{a logical value. If \code{TRUE}, the directory + path is prepended to the file names to give a relative file path. + If \code{FALSE}, the file names (rather than paths) are returned.} + +\item{...}{parameters passed to \code{git_connection()} when +\code{connection} is a path} +} +\description{ +List the files in a path of a git repository +} +\author{ +Thierry Onkelinx, \email{thierry.onkelinx@inbo.be} +} diff --git a/man/read_delim_git.Rd b/man/read_delim_git.Rd new file mode 100644 index 000000000..0685b6b96 --- /dev/null +++ b/man/read_delim_git.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/read_delim_git.r +\docType{methods} +\name{read_delim_git} +\alias{read_delim_git} +\alias{read_delim_git,ANY,ANY-method} +\alias{read_delim_git,git_connection-methods} +\alias{read_delim_git,ANY,git_connection-method} +\alias{read_delim_git,git_connection-methods} +\title{Read a tab delimited file from a git repository} +\usage{ +read_delim_git(file, connection, ...) + +\S4method{read_delim_git}{ANY,ANY}(file, connection, ...) + +\S4method{read_delim_git}{ANY,git_connection}(file, connection, ...) +} +\arguments{ +\item{file}{the name of the file} + +\item{connection}{The path of a git repository or a \code{git_connection} +object} + +\item{...}{parameters passed to \code{git_connection()} when +\code{connection} is a path} +} +\description{ +Read a tab delimited file from a git repository +} +\author{ +Thierry Onkelinx, \email{thierry.onkelinx@inbo.be} +} diff --git a/man/remove_files_git.Rd b/man/remove_files_git.Rd new file mode 100644 index 000000000..9017473a6 --- /dev/null +++ b/man/remove_files_git.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/remove_files_git.r +\docType{methods} +\name{remove_files_git} +\alias{remove_files_git} +\alias{remove_files_git,ANY-method} +\alias{remove_files_git,git_connection-methods} +\alias{remove_files_git,git_connection-method} +\alias{remove_files_git,git_connection-methods} +\title{Remove all the files in a path of a git repository} +\usage{ +remove_files_git(connection, pattern = NULL, ...) + +\S4method{remove_files_git}{ANY}(connection, pattern = NULL, ...) + +\S4method{remove_files_git}{git_connection}(connection, pattern = NULL, ...) +} +\arguments{ +\item{connection}{The path of a git repository or a \code{git_connection} +object} + +\item{pattern}{an optional \link{regular expression}. Only file names + which match the regular expression will be returned.} + +\item{...}{parameters passed to \code{git_connection()} when +\code{connection} is a path} +} +\description{ +Remove all the files in a path of a git repository +} +\author{ +Thierry Onkelinx, \email{thierry.onkelinx@inbo.be} +} diff --git a/man/tree-index-methods.Rd b/man/tree-index-methods.Rd index 86b6fbe88..5ad601c83 100644 --- a/man/tree-index-methods.Rd +++ b/man/tree-index-methods.Rd @@ -64,7 +64,7 @@ tree_object[1] tree_object[1:3] ## Select all blobs in tree -tree_object[sapply(as(tree_object, 'list'), is_blob)] +tree_object[vapply(as(tree_object, 'list'), is_blob, logical(1))] } } \keyword{methods} diff --git a/man/write_delim_git.Rd b/man/write_delim_git.Rd new file mode 100644 index 000000000..f2293dacb --- /dev/null +++ b/man/write_delim_git.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/write_delim_git.r +\docType{methods} +\name{write_delim_git} +\alias{write_delim_git} +\alias{write_delim_git,ANY,ANY,ANY-method} +\alias{write_delim_git,git_connection-methods} +\alias{write_delim_git,ANY,ANY,git_connection-method} +\alias{write_delim_git,git_connection-methods} +\title{Write a dataframe as a tab delimited file to a git repository and stage it} +\usage{ +write_delim_git(x, file, connection, ...) + +\S4method{write_delim_git}{ANY,ANY,ANY}(x, file, connection, ...) + +\S4method{write_delim_git}{ANY,ANY,git_connection}(x, file, connection, ...) +} +\arguments{ +\item{x}{the data.frame} + +\item{file}{the name of the file} + +\item{connection}{The path of a git repository or a \code{git_connection} +object} + +\item{...}{parameters passed to \code{git_connection()} when +\code{connection} is a path. When \code{force} is available it is passed +to \code{add()}.} +} +\value{ +the SHA1 of the file +} +\description{ +The existing file will be overwritten. +} +\author{ +Thierry Onkelinx, \email{thierry.onkelinx@inbo.be} +} diff --git a/tests/testthat.R b/tests/testthat.R new file mode 100644 index 000000000..7e967cd8d --- /dev/null +++ b/tests/testthat.R @@ -0,0 +1,4 @@ +library(testthat) +library(git2r) + +test_check("git2r") diff --git a/tests/testthat/test_a_git_connection.R b/tests/testthat/test_a_git_connection.R new file mode 100644 index 000000000..f016e6dda --- /dev/null +++ b/tests/testthat/test_a_git_connection.R @@ -0,0 +1,194 @@ +context("git_connection") + +tmpdir <- tempfile(pattern = "git2r-git_connection") +connection <- tmpdir +commit.user <- "me" +commit.email <- "me@me.com" +local.path <- "junk" + +# test repo.path +expect_error( + git_connection( + repo.path = connection, + commit.user = commit.user, + commit.email = commit.email + ), + "is not a git repo" +) +dir.create(connection) +expect_error( + git_connection( + repo.path = connection, + commit.user = commit.user, + commit.email = commit.email + ), + "is not a git repo" +) +repo <- git2r::init(connection) + +#test commit.user +expect_error( + git_connection( + repo.path = connection, + commit.user = 1, + commit.email = commit.email + ), + "commit.user is not a string \\(a length one character vector\\)\\." +) +expect_error( + git_connection( + repo.path = connection, + commit.user = NA, + commit.email = commit.email + ), + "commit.user is not a string \\(a length one character vector\\)\\." +) +expect_error( + git_connection( + repo.path = connection, + commit.user = rep(commit.user, 2), + commit.email = commit.email + ), + "commit.user is not a string \\(a length one character vector\\)\\." +) + +# test commit.email +expect_error( + git_connection( + repo.path = connection, + commit.user = commit.user, + commit.email = 1 + ), + "commit.email is not a string \\(a length one character vector\\)\\." +) +expect_error( + git_connection( + repo.path = connection, + commit.user = commit.user, + commit.email = NA + ), + "commit.email is not a string \\(a length one character vector\\)\\." +) +expect_error( + git_connection( + repo.path = connection, + commit.user = commit.user, + commit.email = rep(commit.email, 2) + ), + "commit.email is not a string \\(a length one character vector\\)\\." +) + +expect_is( + git.connection <- git_connection( + repo.path = connection, + commit.user = commit.user, + commit.email = commit.email + ), + "git_connection" +) +expect_identical( + config(repo)$local$user.name, + commit.user +) +expect_identical( + config(repo)$local$user.email, + commit.email +) + +expect_is( + git_connection( + repo.path = connection, + username = "me", + password = "junk", + commit.user = commit.user, + commit.email = commit.email + ), + "git_connection" +) + +expect_error( + git_connection( + repo.path = connection, + username = rep("me", 2), + password = "junk", + commit.user = commit.user, + commit.email = commit.email + ), + "username is not a string \\(a length one character vector\\)\\." +) +expect_error( + git_connection( + repo.path = connection, + username = NA, + password = "junk", + commit.user = commit.user, + commit.email = commit.email + ), + "username is not a string \\(a length one character vector\\)\\." +) +expect_error( + git_connection( + repo.path = connection, + username = "", + password = "junk", + commit.user = commit.user, + commit.email = commit.email + ), + "username not not equal to \"\"" +) + +expect_error( + git_connection( + repo.path = connection, + username = "me", + password = rep("junk", 2), + commit.user = commit.user, + commit.email = commit.email + ), + "password is not a string \\(a length one character vector\\)\\." +) +expect_error( + git_connection( + repo.path = connection, + username = "me", + password = NA, + commit.user = commit.user, + commit.email = commit.email + ), + "password is not a string \\(a length one character vector\\)\\." +) +expect_error( + git_connection( + repo.path = connection, + username = "me", + password = "", + commit.user = commit.user, + commit.email = commit.email + ), + "password not not equal to \"\"" +) +expect_error( + git_connection( + repo.path = connection, + local.path = local.path, + username = "me", + password = "junk", + commit.user = commit.user, + commit.email = commit.email + ), + "is not a directory" +) +dir.create(sprintf("%s/%s", tmpdir, local.path), recursive = TRUE) +expect_is( + z <- git_connection( + repo.path = connection, + local.path = local.path, + username = "me", + password = "junk", + commit.user = commit.user, + commit.email = commit.email + ), + "git_connection" +) +expect_identical(z@LocalPath, local.path) +expect_identical(unlink(tmpdir, recursive = TRUE, force = TRUE), 0L) diff --git a/tests/testthat/test_b_write_delim_git.R b/tests/testthat/test_b_write_delim_git.R new file mode 100644 index 000000000..4d70c3c6f --- /dev/null +++ b/tests/testthat/test_b_write_delim_git.R @@ -0,0 +1,110 @@ +context("write data.frame to git") +describe("write_delim_git()", { + commit.user <- "me" + commit.email <- "me@me.com" + x <- data.frame(0) + x1 <- data.frame(1) + file <- "test.txt" + local.path <- "test/subdir" + tmpdir <- tempfile(pattern = "git2r-write_delim_git") + connection <- normalizePath( + tmpdir, + winslash = "/", + mustWork = FALSE + ) + + it("stops if connection is not a git repository", { + expect_error( + write_delim_git( + x = x, + file = file, + local.path = local.path, + connection = connection, + commit.user = commit.user, + commit.email = commit.email + ), + "is not a git repo" + ) + }) + + dir.create(connection) + repo <- init(connection) + config(repo, user.name = "me", user.email = "me@me.com") + it("stops if the path doesn't exist", { + expect_error( + write_delim_git( + x = x, + file = file, + local.path = local.path, + connection = connection, + commit.user = commit.user, + commit.email = commit.email + ), + "is not a directory" + ) + }) + full.path <- paste(connection, local.path, sep = "/") + dir.create(full.path, recursive = TRUE) + it("stops if x is not a data.frame", { + expect_error( + write_delim_git( + x = matrix(0), + file = file, + local.path = local.path, + connection = connection, + commit.user = commit.user, + commit.email = commit.email + ), + "x does not inherit from class data.frame" + ) + }) + + full.file.path <- paste(connection, local.path, file, sep = "/") + it("returns the sha1 of the file", { + expect_that( + write_delim_git( + x = x, + file = file, + local.path = local.path, + connection = connection, + commit.user = commit.user, + commit.email = commit.email + ), + is_identical_to(hashfile(full.file.path)) + ) + }) + it("can handle tbl_df", { + expect_that( + write_delim_git( + x = dplyr::as.tbl(x), + file = file, + local.path = local.path, + connection = connection, + commit.user = commit.user, + commit.email = commit.email + ), + is_identical_to(hashfile(full.file.path)) + ) + }) + + it("stages the file", { + expect_that( + status(repo)$staged$new, + is_identical_to(paste(local.path, file, sep = "/")) + ) + junk <- commit(repo, "a") + write_delim_git( + x = x1, + file = file, + local.path = local.path, + connection = connection, + commit.user = commit.user, + commit.email = commit.email + ) + expect_that( + status(repo)$staged$modified, + is_identical_to(paste(local.path, file, sep = "/")) + ) + }) + expect_identical(unlink(tmpdir, recursive = TRUE, force = TRUE), 0L) +}) diff --git a/tests/testthat/test_c_git_recent.R b/tests/testthat/test_c_git_recent.R new file mode 100644 index 000000000..9b0c21d4a --- /dev/null +++ b/tests/testthat/test_c_git_recent.R @@ -0,0 +1,54 @@ +context("git_recent") +test_that("git_recent works", { + file <- "test.txt" + local.path <- "test" + tmpdir <- tempfile(pattern = "git2r-git_recent") + connection <- normalizePath( + tmpdir, + winslash = "/", + mustWork = FALSE + ) + df <- data.frame(x = 1, y = 1:10) + + dir.create(paste(connection, local.path, sep = "/"), recursive = TRUE) + repo <- init(connection) + + expect_error( + git_recent( + file = file, + local.path = local.path, + connection = connection, + commit.user = "me", + commit.email = "me@me.com" + ), + "no commits in current branch" + ) + + connection <- git_connection( + repo.path = connection, + local.path = local.path, + commit.user = "me", + commit.email = "me@me.com" + ) + write_delim_git( + x = df, + file = file, + connection = connection + ) + z <- commit(connection, "test") + expect_is( + x <- git_recent(file = file, connection = connection), + "list" + ) + + expect_identical( + x, + list( + Commit = z@sha, + Author = sprintf("%s <%s>", z@author@name, z@author@email), + Date = as.POSIXct(as(z@author@when, "character")) + ) + ) + + expect_identical(unlink(tmpdir, recursive = TRUE, force = TRUE), 0L) +}) diff --git a/tests/testthat/test_c_git_sha.R b/tests/testthat/test_c_git_sha.R new file mode 100644 index 000000000..52a90dca1 --- /dev/null +++ b/tests/testthat/test_c_git_sha.R @@ -0,0 +1,64 @@ +context("git_sha") +test_that("git_sha returns the correct value", { + file <- "test.txt" + local.path <- "test" + tmpdir <- tempfile(pattern = "git2r-git_sha") + connection <- normalizePath( + tmpdir, + winslash = "/", + mustWork = FALSE + ) + df <- data.frame(x = 1, y = 1:10) + + expect_error( + git_sha( + file = file, + connection = connection, + commit.user = "me", + commit.email = "me@me.com" + ), + "is not a git repo" + ) + + dir.create(paste(connection, local.path, sep = "/"), recursive = TRUE) + repo <- init(connection) + expect_error( + git_sha( + file = file, + connection = connection, + commit.user = "me", + commit.email = "me@me.com" + ), + "no commits available" + ) + + connection <- git_connection( + repo.path = connection, + local.path = local.path, + commit.user = "me", + commit.email = "me@me.com" + ) + hash <- write_delim_git( + x = df, + file = file, + connection = connection + ) + commit(connection, message = "test") + expect_is( + sha <- git_sha( + file = file, + connection = connection + ), + "data.frame" + ) + expect_identical( + sha, + data.frame( + SHA = hash, + Path = local.path, + File = file, + stringsAsFactors = FALSE + ) + ) + expect_identical(unlink(tmpdir, recursive = TRUE, force = TRUE), 0L) +}) diff --git a/tests/testthat/test_c_list_files_git.R b/tests/testthat/test_c_list_files_git.R new file mode 100644 index 000000000..3d4ee06b9 --- /dev/null +++ b/tests/testthat/test_c_list_files_git.R @@ -0,0 +1,88 @@ +context("list files for a git repository") +describe("list_files_git()", { + files <- sort(c("test.txt", "0123456.txt", "test", "0123456")) + local.path <- "test" + tmpdir <- tempfile(pattern = "git2r-list_files_git") + connection <- normalizePath( + tmpdir, + winslash = "/", + mustWork = FALSE + ) + commit.user <- "me" + commit.email <- "me@me.com" + + + it("stops if connection is not a git repository", { + expect_error( + list_files_git( + local.path = local.path, + connection = connection, + commit.user = commit.user, + commit.email = commit.email + ), + "is not a git repo" + ) + }) + + dir.create(connection) + repo <- git2r::init(connection) + connection <- normalizePath(connection, winslash = "/", mustWork = FALSE) + full.path <- paste(connection, local.path, sep = "/") + full.path <- normalizePath(full.path, winslash = "/", mustWork = FALSE) + it("stops if the local.path doesn't exist", { + expect_error( + list_files_git( + local.path = local.path, + connection = connection, + commit.user = commit.user, + commit.email = commit.email + ), + "is not a directory" + ) + }) + + dir.create(paste(connection, local.path, sep = "/")) + file.create(paste(full.path, files, sep = "/")) + it("list the files according to the pattern", { + expect_that( + list_files_git( + local.path = local.path, + pattern = "^[0123456789].*\\.txt$", + connection = connection, + commit.user = commit.user, + commit.email = commit.email + ), + is_identical_to(files[grep("^[0123456789].*\\.txt$", files)]) + ) + expect_that( + list_files_git( + local.path = local.path, + pattern = "\\.txt$", + connection = connection, + commit.user = commit.user, + commit.email = commit.email + ), + is_identical_to(files[grep("\\.txt$", files)]) + ) + expect_that( + list_files_git( + local.path = local.path, + connection = connection, + commit.user = commit.user, + commit.email = commit.email + ), + is_identical_to(files) + ) + expect_that( + list_files_git( + local.path = local.path, + pattern = ".exe", + connection = connection, + commit.user = commit.user, + commit.email = commit.email + ), + is_identical_to(character(0)) + ) + }) + expect_identical(unlink(tmpdir, recursive = TRUE, force = TRUE), 0L) +}) diff --git a/tests/testthat/test_c_read_delim_git.R b/tests/testthat/test_c_read_delim_git.R new file mode 100644 index 000000000..f2378ff76 --- /dev/null +++ b/tests/testthat/test_c_read_delim_git.R @@ -0,0 +1,63 @@ +context("read data.frame from git") +describe("read_delim_git()", { + file <- "test.txt" + local.path <- "test" + tmpdir <- tempfile(pattern = "git2r-read_delim_git") + connection <- normalizePath( + tmpdir, + winslash = "/", + mustWork = FALSE + ) + df <- data.frame(x = 1, y = 1:10) + + + it("stops if connection is not a git repository", { + expect_error( + read_delim_git( + file = file, + local.path = local.path, + connection = connection, + commit.user = "me", + commit.email = "me@me.com" + ), + "is not a git repo" + ) + }) + + dir.create(paste(connection, local.path, sep = "/"), recursive = TRUE) + repo <- init(connection) + + it("returns FALSE when the file doesn't exists", { + expect_error( + read_delim_git( + file = file, + local.path = local.path, + connection = connection, + commit.user = "me", + commit.email = "me@me.com" + ), + paste0(repo@path, "/", local.path, "/", file) + ) + }) + write_delim_git( + x = df, + file = file, + local.path = local.path, + connection = connection, + commit.user = "me", + commit.email = "me@me.com" + ) + it("read the tab-delimited file", { + expect_that( + read_delim_git( + file = file, + local.path = local.path, + connection = connection, + commit.user = "me", + commit.email = "me@me.com" + ), + is_equivalent_to(df) + ) + }) + expect_identical(unlink(tmpdir, recursive = TRUE, force = TRUE), 0L) +}) diff --git a/tests/testthat/test_d_remove_files_git.R b/tests/testthat/test_d_remove_files_git.R new file mode 100644 index 000000000..a65453492 --- /dev/null +++ b/tests/testthat/test_d_remove_files_git.R @@ -0,0 +1,124 @@ +context("remove_files_git") +test_that("remove_files_git returns the correct value", { + file <- "test.txt" + local.path <- "test" + tmpdir <- tempfile(pattern = "git2r-remove_files_git") + connection <- normalizePath( + tmpdir, + winslash = "/", + mustWork = FALSE + ) + df <- data.frame(x = 1, y = 1:10) + pattern <- "txt$" + + expect_error( + remove_files_git( + connection = connection, + commit.user = "me", + commit.email = "me@me.com" + ), + "is not a git repo" + ) + + dir.create(paste(connection, local.path, sep = "/"), recursive = TRUE) + repo <- init(connection) + + git_con <- git_connection( + repo.path = connection, + local.path = local.path, + commit.user = "me", + commit.email = "me@me.com" + ) + hash <- write_delim_git( + x = df, + file = file, + connection = git_con, + commit.user = "me", + commit.email = "me@me.com" + ) + z <- commit(git_con, message = "test") + expect_true( + remove_files_git( + connection = connection, + local.path = local.path, + commit.user = "me", + commit.email = "me@me.com", + pattern = "junk" + ) + ) + expect_identical( + list_files_git(git_con), + file + ) + expect_true( + remove_files_git( + connection = git_con, + pattern = pattern + ) + ) + expect_identical( + list_files_git(git_con), + character(0) + ) + expect_identical( + status(git_con@Repository)$staged$deleted, + paste(local.path, file, sep = "/") + ) + + expect_identical(unlink(tmpdir, recursive = TRUE, force = TRUE), 0L) +}) + +test_that("returns an error when removing files fails", { + skip_on_os(c("windows", "mac", "solaris")) + + file <- "test.txt" + local.path <- "test" + tmpdir <- tempfile(pattern = "git2r-remove_files_git_error") + connection <- normalizePath( + tmpdir, + winslash = "/", + mustWork = FALSE + ) + df <- data.frame(x = 1, y = 1:10) + pattern <- "txt$" + dir.create(paste(connection, local.path, sep = "/"), recursive = TRUE) + repo <- init(connection) + + git_con <- git_connection( + repo.path = connection, + local.path = local.path, + commit.user = "me", + commit.email = "me@me.com" + ) + hash <- write_delim_git( + x = df, + file = file, + connection = git_con + ) + z <- commit(git_con, message = "test") + + Sys.chmod( + paste(git_con@Repository@path, git_con@LocalPath, sep = "/"), + mode = "0400" + ) + expect_error( + remove_files_git( + connection = git_con, + pattern = pattern + ), + "Error cleaning existing files" + ) + + Sys.chmod( + paste(git_con@Repository@path, git_con@LocalPath, sep = "/"), + mode = "0777" + ) + expect_true( + remove_files_git( + connection = git_con, + pattern = pattern + ), + "Error cleaning existing files" + ) + expect_identical(unlink(tmpdir, recursive = TRUE, force = TRUE), 0L) +})