diff --git a/DESCRIPTION b/DESCRIPTION index 9319578..2186725 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -32,6 +32,6 @@ VignetteBuilder: knitr Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.3.9000 SystemRequirements: libgit2 (>= 1.0): libgit2-devel (rpm) or libgit2-dev (deb) Language: en-US +Config/roxygen2/version: 8.0.0 diff --git a/NAMESPACE b/NAMESPACE index 21203e9..cab14ca 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -32,8 +32,10 @@ export(git_config_get) export(git_config_global) export(git_config_global_get) export(git_config_global_set) +export(git_config_global_unset) export(git_config_local_get) export(git_config_set) +export(git_config_unset) export(git_conflicts) export(git_diff) export(git_diff_patch) @@ -128,6 +130,7 @@ useDynLib(gert,R_git_commit_log) useDynLib(gert,R_git_commit_stats) useDynLib(gert,R_git_config_list) useDynLib(gert,R_git_config_set) +useDynLib(gert,R_git_config_unset) useDynLib(gert,R_git_conflict_list) useDynLib(gert,R_git_create_branch) useDynLib(gert,R_git_delete_branch) diff --git a/R/config.R b/R/config.R index fbdb2ab..c19cb04 100644 --- a/R/config.R +++ b/R/config.R @@ -1,16 +1,16 @@ #' Get or set Git configuration #' #' @description -#' Get or set Git options, as `git config` does on the command line. **Global** -#' settings affect all of a user's Git operations (`git config --global`), -#' whereas **local** settings are scoped to a specific repository (`git config -#' --local`). When both exist, local options always win. +#' Get, set, or unset Git options, as `git config` does on the command line. +#' **Global** settings affect all of a user's Git operations +#' (`git config --global`), whereas **local** settings are scoped to a specific +#' repository (`git config --local`). When both exist, local options always win. #' #' ```{r echo = FALSE, results = "asis"} #' dat <- data.frame( -#' local = c("`git_config()`", "`git_config_get()`", "`git_config_local_get()`", "`git_config_set()`"), -#' global = c("`git_config_global()`", "`git_config_get()`", "`git_config_global_get()`", "`git_config_global_set()`"), -#' row.names = c("get all", "get one (local+global)", "get one (local or global only)", "set") +#' local = c("`git_config()`", "`git_config_get()`", "`git_config_local_get()`", "`git_config_set()`", "`git_config_unset()`"), +#' global = c("`git_config_global()`", "`git_config_get()`", "`git_config_global_get()`", "`git_config_global_set()`", "`git_config_global_unset()`"), +#' row.names = c("get all", "get one (local+global)", "get one (local or global only)", "set", "unset") #' ) #' knitr::kable(dat, col.names = paste0("**", colnames(dat), "**")) #' ``` @@ -28,6 +28,8 @@ #' * `git_config_set()`, `git_config_global_set()`: The previous value(s) of #' `name` in local or global config, respectively. If this option was #' previously unset, returns `NULL`. Returns invisibly. +#' * `git_config_unset()`, `git_config_global_unset()`: The previous value(s) of +#' `name` that were unset. Returns invisibly. #' #' @note All entries in the `name` column are automatically normalised to #' lowercase (see @@ -146,6 +148,21 @@ git_config_global_set <- function(name, value, add = FALSE) { } } +#' @export +#' @rdname git_config +#' @useDynLib gert R_git_config_unset +#' @param pattern optional regular expression, for matching values to unset in +#' case of multiple values +git_config_unset <- function(name, pattern = NULL, repo = '.'){ + .Call(R_git_config_unset, git_open(repo), name, pattern) +} + +#' @export +#' @rdname git_config +git_config_global_unset <- function(name, pattern = NULL){ + .Call(R_git_config_unset, NULL, name, pattern) +} + #' Show libgit2 version and capabilities #' #' `libgit2_config()` reveals which version of libgit2 gert is using and which diff --git a/man/git_config.Rd b/man/git_config.Rd index fac4dda..a313c92 100644 --- a/man/git_config.Rd +++ b/man/git_config.Rd @@ -8,6 +8,8 @@ \alias{git_config_global_get} \alias{git_config_set} \alias{git_config_global_set} +\alias{git_config_unset} +\alias{git_config_global_unset} \title{Get or set Git configuration} \usage{ git_config(repo = ".") @@ -23,6 +25,10 @@ git_config_global_get(name) git_config_set(name, value, add = FALSE, repo = ".") git_config_global_set(name, value, add = FALSE) + +git_config_unset(name, pattern = NULL, repo = ".") + +git_config_global_unset(name, pattern = NULL) } \arguments{ \item{repo}{The path to the git repository. If the directory is not a @@ -39,6 +45,9 @@ unset).} \item{add}{if \code{TRUE}, append a new entry for \code{name} instead of replacing existing one(s). Equivalent to \verb{git config --add}. Only supported for string values.} + +\item{pattern}{optional regular expression, for matching values to unset in +case of multiple values} } \value{ \itemize{ @@ -55,17 +64,21 @@ local (repository-level) config only. \item \code{git_config_set()}, \code{git_config_global_set()}: The previous value(s) of \code{name} in local or global config, respectively. If this option was previously unset, returns \code{NULL}. Returns invisibly. +\item \code{git_config_unset()}, \code{git_config_global_unset()}: The previous value(s) of +\code{name} that were unset. Returns invisibly. } } \description{ -Get or set Git options, as \verb{git config} does on the command line. \strong{Global} -settings affect all of a user's Git operations (\verb{git config --global}), -whereas \strong{local} settings are scoped to a specific repository (\verb{git config --local}). When both exist, local options always win.\tabular{lll}{ +Get, set, or unset Git options, as \verb{git config} does on the command line. +\strong{Global} settings affect all of a user's Git operations +(\verb{git config --global}), whereas \strong{local} settings are scoped to a specific +repository (\verb{git config --local}). When both exist, local options always win.\tabular{lll}{ \tab \strong{local} \tab \strong{global} \cr get all \tab \code{git_config()} \tab \code{git_config_global()} \cr get one (local+global) \tab \code{git_config_get()} \tab \code{git_config_get()} \cr get one (local or global only) \tab \code{git_config_local_get()} \tab \code{git_config_global_get()} \cr set \tab \code{git_config_set()} \tab \code{git_config_global_set()} \cr + unset \tab \code{git_config_unset()} \tab \code{git_config_global_unset()} \cr } } \note{ diff --git a/src/config.c b/src/config.c index 2bc9808..cf7f35e 100644 --- a/src/config.c +++ b/src/config.c @@ -104,3 +104,21 @@ SEXP R_git_config_set(SEXP ptr, SEXP name, SEXP value, SEXP add){ git_config_free(cfg); return R_NilValue; } + +SEXP R_git_config_unset(SEXP ptr, SEXP name, SEXP pattern){ + git_config *cfg = NULL; + const char *cname = CHAR(STRING_ELT(name, 0)); + if(Rf_isNull(ptr)) { + bail_if(git_config_open_default(&cfg), "git_config_open_default"); + } else { + bail_if(git_repository_config(&cfg, get_git_repository(ptr)),"git_repository_config"); + } + if(Rf_length(pattern)){ + const char *cpattern = CHAR(STRING_ELT(pattern, 0)); + bail_if(git_config_delete_multivar(cfg, cname, cpattern), "git_config_delete_multivar"); + } else { + bail_if(git_config_delete_entry(cfg, cname), "git_config_delete_entry"); + } + git_config_free(cfg); + return R_NilValue; +} diff --git a/src/init.c b/src/init.c index 5703c2d..393d0d4 100644 --- a/src/init.c +++ b/src/init.c @@ -31,6 +31,7 @@ extern SEXP R_git_commit_log(SEXP, SEXP, SEXP, SEXP, SEXP); extern SEXP R_git_commit_stats(SEXP, SEXP); extern SEXP R_git_config_list(SEXP); extern SEXP R_git_config_set(SEXP, SEXP, SEXP, SEXP); +extern SEXP R_git_config_unset(SEXP, SEXP, SEXP); extern SEXP R_git_conflict_list(SEXP); extern SEXP R_git_create_branch(SEXP, SEXP, SEXP, SEXP, SEXP); extern SEXP R_git_delete_branch(SEXP, SEXP); @@ -116,6 +117,7 @@ static const R_CallMethodDef CallEntries[] = { {"R_git_commit_stats", (DL_FUNC) &R_git_commit_stats, 2}, {"R_git_config_list", (DL_FUNC) &R_git_config_list, 1}, {"R_git_config_set", (DL_FUNC) &R_git_config_set, 4}, + {"R_git_config_unset", (DL_FUNC) &R_git_config_unset, 3}, {"R_git_conflict_list", (DL_FUNC) &R_git_conflict_list, 1}, {"R_git_create_branch", (DL_FUNC) &R_git_create_branch, 5}, {"R_git_delete_branch", (DL_FUNC) &R_git_delete_branch, 2}, diff --git a/src/utils.h b/src/utils.h index 52ab8f9..646d8eb 100644 --- a/src/utils.h +++ b/src/utils.h @@ -23,7 +23,7 @@ git_strarray *files_to_array(SEXP files); #define build_tibble(...) list_to_tibble(build_list( __VA_ARGS__)) -#define AT_LEAST_LIBGIT2(x,y) (LIBGIT2_VER_MAJOR > x || LIBGIT2_VER_MINOR >= y) +#define AT_LEAST_LIBGIT2(x,y) (LIBGIT2_VER_MAJOR > x || (LIBGIT2_VER_MAJOR == x && LIBGIT2_VER_MINOR >= y)) /* Workaround for API change in 1.8.0 and 1.8.1 only: https://github.com/libgit2/libgit2/issues/6793 */ #if LIBGIT2_VER_MAJOR == 1 && LIBGIT2_VER_MINOR == 8 && LIBGIT2_VER_REVISION < 2 diff --git a/tests/testthat/test-config.R b/tests/testthat/test-config.R index caffcc3..fc9f13f 100644 --- a/tests/testthat/test-config.R +++ b/tests/testthat/test-config.R @@ -38,3 +38,18 @@ test_that("local, custom config roundtrip", { expect_equal(orig, "ccc") expect_null(git_config_get("aaa.bbb", repo = repo)) }) + +test_that("git_config_unset() works for multivar options", { + repo <- git_init(tempfile("gert-tests-config")) + on.exit(unlink(repo, recursive = TRUE)) + + git_config_set("aaa.bbb", "ccc", add = TRUE, repo = repo) + git_config_set("aaa.bbb", "ccccc", add = TRUE, repo = repo) + git_config_set("aaa.bbb", "ddd", add = TRUE, repo = repo) + + git_config_unset("aaa.bbb", "ccc", repo = repo) + expect_equal(git_config_get("aaa.bbb", repo = repo), "ddd") + + git_config_unset("aaa.bbb", repo = repo) + expect_null(git_config_get("aaa.bbb", repo = repo)) +})