1## git2r, R bindings to the libgit2 library. 2## Copyright (C) 2013-2019 The git2r contributors 3## 4## This program is free software; you can redistribute it and/or modify 5## it under the terms of the GNU General Public License, version 2, 6## as published by the Free Software Foundation. 7## 8## git2r is distributed in the hope that it will be useful, 9## but WITHOUT ANY WARRANTY; without even the implied warranty of 10## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11## GNU General Public License for more details. 12## 13## You should have received a copy of the GNU General Public License along 14## with this program; if not, write to the Free Software Foundation, Inc., 15## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 16 17##' Add file(s) to index 18##' 19##' @template repo-param 20##' @param path Character vector with file names or shell glob 21##' patterns that will matched against files in the repository's 22##' working directory. Each file that matches will be added to the 23##' index (either updating an existing entry or adding a new 24##' entry). 25##' @param force Add ignored files. Default is FALSE. 26##' @return invisible(NULL) 27##' @export 28##' @examples 29##' \dontrun{ 30##' ## Initialize a repository 31##' path <- tempfile(pattern="git2r-") 32##' dir.create(path) 33##' repo <- init(path) 34##' 35##' ## Create a user 36##' config(repo, user.name = "Alice", user.email = "alice@@example.org") 37##' 38##' ## Create a file 39##' writeLines("a", file.path(path, "a.txt")) 40##' 41##' ## Add file to repository and view status 42##' add(repo, "a.txt") 43##' status(repo) 44##' 45##' ## Add file with a leading './' when the repository working 46##' ## directory is the current working directory 47##' setwd(path) 48##' writeLines("b", file.path(path, "b.txt")) 49##' add(repo, "./b.txt") 50##' status(repo) 51##' 52##' ## Add a file in a sub-folder with sub-folder as the working 53##' ## directory. Create a file in the root of the repository 54##' ## working directory that will remain untracked. 55##' dir.create(file.path(path, "sub_dir")) 56##' setwd("./sub_dir") 57##' writeLines("c", file.path(path, "c.txt")) 58##' writeLines("c", file.path(path, "sub_dir/c.txt")) 59##' add(repo, "c.txt") 60##' status(repo) 61##' 62##' ## Add files with glob expansion when the current working 63##' ## directory is outside the repository's working directory. 64##' setwd(tempdir()) 65##' dir.create(file.path(path, "glob_dir")) 66##' writeLines("d", file.path(path, "glob_dir/d.txt")) 67##' writeLines("e", file.path(path, "glob_dir/e.txt")) 68##' writeLines("f", file.path(path, "glob_dir/f.txt")) 69##' writeLines("g", file.path(path, "glob_dir/g.md")) 70##' add(repo, "glob_dir/*txt") 71##' status(repo) 72##' 73##' ## Add file with glob expansion with a relative path when 74##' ## the current working directory is inside the repository's 75##' ## working directory. 76##' setwd(path) 77##' add(repo, "./glob_dir/*md") 78##' status(repo) 79##' } 80add <- function(repo = ".", path = NULL, force = FALSE) { 81 ## Documentation for the pathspec argument in the libgit2 function 82 ## 'git_index_add_all' that git2r use internally: 83 ## 84 ## The pathspec is a list of file names or shell glob patterns 85 ## that will matched against files in the repository's working 86 ## directory. Each file that matches will be added to the index 87 ## (either updating an existing entry or adding a new entry). 88 89 if (is.null(path) || !is.character(path)) 90 stop("'path' must be a character vector") 91 92 repo <- lookup_repository(repo) 93 repo_wd <- normalizePath(workdir(repo), winslash = "/") 94 path <- vapply(path, sanitize_path, character(1), repo_wd = repo_wd) 95 96 .Call(git2r_index_add_all, repo, path, isTRUE(force)) 97 98 invisible(NULL) 99} 100 101sanitize_path <- function(p, repo_wd) { 102 np <- suppressWarnings(normalizePath(p, winslash = "/")) 103 104 if (!length(grep("/$", repo_wd))) 105 repo_wd <- paste0(repo_wd, "/") 106 107 ## Check if the normalized path is a non-file e.g. a glob. 108 if (!file.exists(np)) { 109 ## Check if the normalized path starts with a leading './' 110 if (length(grep("^[.]/", np))) { 111 nd <- suppressWarnings(normalizePath(dirname(p), winslash = "/")) 112 if (!length(grep("/$", nd))) 113 nd <- paste0(nd, "/") 114 np <- paste0(nd, basename(np)) 115 } 116 } 117 118 ## Check if the file is in the repository's working directory, 119 ## else let libgit2 handle this path unmodified. 120 if (!length(grep(paste0("^", repo_wd), np))) 121 return(p) 122 123 ## Change the path to be relative to the repository's working 124 ## directory. Substitute common prefix with "" 125 sub(paste0("^", repo_wd), "", np) 126} 127 128##' Remove files from the working tree and from the index 129##' 130##' @template repo-param 131##' @param path character vector with filenames to remove. Only files 132##' known to Git are removed. 133##' @return invisible(NULL) 134##' @export 135##' @examples 136##' \dontrun{ 137##' ## Initialize a repository 138##' path <- tempfile(pattern="git2r-") 139##' dir.create(path) 140##' repo <- init(path) 141##' 142##' ## Create a user 143##' config(repo, user.name = "Alice", user.email = "alice@@example.org") 144##' 145##' ## Create a file 146##' writeLines("Hello world!", file.path(path, "file-to-remove.txt")) 147##' 148##' ## Add file to repository 149##' add(repo, "file-to-remove.txt") 150##' commit(repo, "First commit message") 151##' 152##' ## Remove file 153##' rm_file(repo, "file-to-remove.txt") 154##' 155##' ## View status of repository 156##' status(repo) 157##' } 158rm_file <- function(repo = ".", path = NULL) { 159 if (is.null(path) || !is.character(path)) 160 stop("'path' must be a character vector") 161 162 repo <- lookup_repository(repo) 163 164 if (length(path)) { 165 repo_wd <- workdir(repo) 166 repo_wd <- normalizePath(workdir(repo), winslash = "/") 167 path <- vapply(path, sanitize_path, character(1), repo_wd = repo_wd) 168 169 ## Check that files exists and are known to Git 170 if (!all(file.exists(file.path(repo_wd, path)))) { 171 stop(sprintf("pathspec '%s' did not match any files. ", 172 path[!file.exists(file.path(repo_wd, path))])) 173 } 174 175 if (any(file.info(file.path(repo_wd, path))$isdir)) { 176 stop(sprintf("pathspec '%s' did not match any files. ", 177 path[exists(file.path(repo_wd, path))])) 178 } 179 180 s <- status(repo, staged = TRUE, unstaged = TRUE, 181 untracked = TRUE, ignored = TRUE) 182 if (any(path %in% c(s$ignored, s$untracked))) { 183 stop(sprintf("pathspec '%s' did not match any files. ", 184 path[path %in% c(s$ignored, s$untracked)])) 185 } 186 187 if (any(path %in% s$staged)) { 188 stop(sprintf("'%s' has changes staged in the index. ", 189 path[path %in% s$staged])) 190 } 191 192 if (any(path %in% s$unstaged)) { 193 stop(sprintf("'%s' has local modifications. ", 194 path[path %in% s$unstaged])) 195 } 196 197 ## Remove and stage files 198 lapply(path, function(x) { 199 file.remove(file.path(repo_wd, x)) 200 .Call(git2r_index_remove_bypath, repo, x) 201 }) 202 } 203 204 invisible(NULL) 205} 206 207##' Remove an index entry corresponding to a file on disk 208##' 209##' @template repo-param 210##' @param path character vector with filenames to remove. The path 211##' must be relative to the repository's working folder. It may 212##' exist. If this file currently is the result of a merge 213##' conflict, this file will no longer be marked as 214##' conflicting. The data about the conflict will be moved to the 215##' "resolve undo" (REUC) section. 216##' @return invisible(NULL) 217##' @export 218##' @examples 219##' \dontrun{ 220##' ## Initialize a repository 221##' path <- tempfile(pattern="git2r-") 222##' dir.create(path) 223##' repo <- init(path) 224##' 225##' ## Create a user 226##' config(repo, user.name = "Alice", user.email = "alice@@example.org") 227##' 228##' ## Create a file 229##' writeLines("Hello world!", file.path(path, "file-to-remove.txt")) 230##' 231##' ## Add file to repository 232##' add(repo, "file-to-remove.txt") 233##' 234##' ## View status of repository 235##' status(repo) 236##' 237##' ## Remove file 238##' index_remove_bypath(repo, "file-to-remove.txt") 239##' 240##' ## View status of repository 241##' status(repo) 242##' } 243index_remove_bypath <- function(repo = ".", path = NULL) { 244 .Call(git2r_index_remove_bypath, lookup_repository(repo), path) 245 invisible(NULL) 246} 247