Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ export(git_commit_id)
export(git_commit_info)
export(git_commit_stats)
export(git_config)
export(git_config_get)
export(git_config_global)
export(git_config_global_get)
export(git_config_global_set)
export(git_config_local_get)
export(git_config_set)
export(git_conflicts)
export(git_diff)
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# gert (development version)

- Add `git_config_get()`, `git_config_local_get()`, and `git_config_global_get()` to retrieve a single named config option, returning `NULL` if unset (#267).
- `git_clone()` without a `path` argument now clones into a directory named after the
"humanish" part of the URL, so "git@github.com:francisbarton/myrepo.git" gets cloned into `myrepo` (@francisbardon, #192).
- `git_remote_set_pushurl()` gains an `add` argument to append push URLs instead of replacing them. (@robitalec, #128)
Expand Down
85 changes: 59 additions & 26 deletions R/config.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
#' 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. Four functions address
#' the four possible combinations of getting vs setting and global vs. local.
#' --local`). When both exist, local options always win.
#'
#' ```{r echo = FALSE, results = "asis"}
#' dat <- data.frame(
#' local = c("`git_config()`", "`git_config_set()`"),
#' global = c("`git_config_global()`", "`git_config_global_set()`"),
#' row.names = c("get", "set")
#' 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")
#' )
#' knitr::kable(dat, col.names = paste0("**", colnames(dat), "**"))
#' ```
Expand All @@ -21,6 +20,11 @@
#' option is determined from global or local config.
#' * `git_config_global()`: a `data.frame`, as for `git_config()`, except only
#' for global Git options.
#' * `git_config_get()`: the value of the named option considering both local and
#' global config (local wins), or `NULL` if unset.
#' * `git_config_local_get()`: as for `git_config_get()`, but restricted to
#' local (repository-level) config only.
#' * `git_config_global_get()`: as for `git_config_get()`, but for global config only.
#' * `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.
Expand All @@ -36,15 +40,16 @@
#'
#' previous <- git_config_set("aaa.bbb", "ccc", repo = r)
#' previous
#' cfg <- git_config(repo = r)
#' subset(cfg, level == "local")
#' cfg$value[cfg$name == "aaa.bbb"]
#' git_config_local_get("aaa.bbb", repo = r)
#'
#' previous <- git_config_set("aaa.bbb", NULL, repo = r)
#' previous
#' cfg <- git_config(repo = r)
#' subset(cfg, level == "local")
#' cfg$value[cfg$name == "aaa.bbb"]
#' git_config_local_get("aaa.bbb", repo = r)
#'
#' # Get a single named option (returns NULL if unset)
#' git_config_get("aaa.bbb", repo = r)
#' git_config_set("aaa.bbb", "ccc", repo = r)
#' git_config_get("aaa.bbb", repo = r)
#'
#' unlink(r, recursive = TRUE)
#'
Expand All @@ -53,6 +58,10 @@
#' git_config_global_set("user.name", "Your Name")
#' git_config_global_set("user.email", "your@email.com")
#' git_config_global()
#'
#' # Get a single global option (returns NULL if unset)
#' git_config_global_get("user.name")
#' git_config_global_get("gert.nonexistent")
#' }
#' @export
#' @family git
Expand All @@ -70,10 +79,41 @@ git_config_global <- function() {
.Call(R_git_config_list, NULL)
}

#' @export
#' @rdname git_config
#' @param name Name of the option to get or set
git_config_get <- function(name, repo = '.') {
cfg <- git_config(repo = repo)
if (!name %in% cfg$name) {
return(NULL)
}
cfg$value[cfg$name == name]
}

#' @export
#' @rdname git_config
git_config_local_get <- function(name, repo = '.') {
cfg <- git_config(repo = repo)
cfg <- cfg[cfg$level == "local", ]
if (!name %in% cfg$name) {
return(NULL)
}
cfg$value[cfg$name == name]
}

#' @export
#' @rdname git_config
git_config_global_get <- function(name) {
cfg <- git_config_global()
if (!name %in% cfg$name) {
return(NULL)
}
cfg$value[cfg$name == name]
}

#' @export
#' @rdname git_config
#' @useDynLib gert R_git_config_set
#' @param name Name of the option to set
#' @param value Value to set. Must be a string, logical, number or `NULL` (to
#' unset).
#' @param add if `TRUE`, append a new entry for `name` instead of replacing
Expand All @@ -85,8 +125,7 @@ git_config_set <- function(name, value, add = FALSE, repo = '.') {
}
repo <- git_open(repo)
name <- as.character(name)
orig_cfg <- git_config(repo = repo)
out <- orig_cfg$value[orig_cfg$name == name & orig_cfg$level == "local"]
out <- git_config_local_get(name, repo = repo)
.Call(R_git_config_set, repo, name, value, add)
if (length(out) > 0) {
invisible(out)
Expand All @@ -98,8 +137,7 @@ git_config_set <- function(name, value, add = FALSE, repo = '.') {
#' @export
#' @rdname git_config
git_config_global_set <- function(name, value, add = FALSE) {
orig_cfg <- git_config_global()
out <- orig_cfg$value[orig_cfg$name == name]
out <- git_config_global_get(name)
.Call(R_git_config_set, NULL, name, value, add)
if (length(out) > 0) {
invisible(out)
Expand Down Expand Up @@ -141,9 +179,8 @@ configure_global_user <- function() {
}

global_user_is_configured <- function() {
cfg <- git_config_global()
user_name_exists <- any(cfg$name == "user.name")
user_email_exists <- any(cfg$name == "user.email")
user_name_exists <- !is.null(git_config_global_get("user.name"))
user_email_exists <- !is.null(git_config_global_get("user.email"))
user_name_exists && user_email_exists
}

Expand All @@ -161,14 +198,10 @@ global_user_is_configured <- function() {
#' `FALSE` otherwise.
#'
#' @export
#' @examples
#' @examplesIf interactive()
#' user_is_configured()
user_is_configured <- function(repo = ".") {
cfg <- tryCatch(
git_config(repo),
error = function(e) git_config_global()
)
user_name_exists <- any(cfg$name == "user.name")
user_email_exists <- any(cfg$name == "user.email")
user_name_exists <- !is.null(git_config_get("user.name", repo = repo))
user_email_exists <- !is.null(git_config_get("user.email", repo = repo))
user_name_exists && user_email_exists
}
40 changes: 30 additions & 10 deletions man/git_config.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions man/user_is_configured.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 30 additions & 4 deletions tests/testthat/test-config.R
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
test_that("git_config_get returns value or NULL", {
repo <- git_init(tempfile("gert-tests-config"))
on.exit(unlink(repo, recursive = TRUE))

expect_null(git_config_get("aaa.bbb", repo = repo))
git_config_set("aaa.bbb", "ccc", repo = repo)
expect_equal(git_config_get("aaa.bbb", repo = repo), "ccc")
})

test_that("git_config_global_get returns value or NULL", {
expect_null(git_config_global_get("gert.nonexistent.option.xyzzy"))
# Note: avoid setting/unsetting real global config in tests
})

test_that("git_config_local_get returns local value or NULL", {
repo <- git_init(tempfile("gert-tests-config"))
on.exit(unlink(repo, recursive = TRUE))

expect_null(git_config_local_get("aaa.bbb", repo = repo))
git_config_set("aaa.bbb", "ccc", repo = repo)
expect_equal(git_config_local_get("aaa.bbb", repo = repo), "ccc")
# global-only option should not be visible at local level
expect_null(git_config_local_get(
"gert.nonexistent.option.xyzzy",
repo = repo
))
})

test_that("local, custom config roundtrip", {
repo <- git_init(tempfile("gert-tests-config"))
on.exit(unlink(repo, recursive = TRUE))

orig <- git_config_set("aaa.bbb", "ccc", repo = repo)
expect_null(orig)
cfg <- git_config(repo)
expect_equal(cfg$value[cfg$name == "aaa.bbb"], "ccc")
expect_equal(git_config_get("aaa.bbb", repo = repo), "ccc")

orig <- git_config_set("aaa.bbb", NULL, repo = repo)
expect_equal(orig, "ccc")
cfg <- git_config(repo)
expect_equal(cfg$value[cfg$name == "aaa.bbb"], character())
expect_null(git_config_get("aaa.bbb", repo = repo))
})
36 changes: 27 additions & 9 deletions tests/testthat/test-remotes.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,23 @@ test_that("git_remote_set_pushurl with add = TRUE appends push URLs", {

git_remote_add("https://example.com/fetch", name = "origin", repo = repo)

git_remote_set_pushurl("https://example.com/push1", remote = "origin", repo = repo)
git_remote_set_pushurl("https://example.com/push2", remote = "origin", add = TRUE, repo = repo)
git_remote_set_pushurl(
"https://example.com/push1",
remote = "origin",
repo = repo
)
git_remote_set_pushurl(
"https://example.com/push2",
remote = "origin",
add = TRUE,
repo = repo
)

cfg <- git_config(repo = repo)
pushurls <- cfg$value[cfg$name == "remote.origin.pushurl"]
expect_setequal(pushurls, c("https://example.com/push1", "https://example.com/push2"))
pushurls <- git_config_get("remote.origin.pushurl", repo = repo)
expect_setequal(
pushurls,
c("https://example.com/push1", "https://example.com/push2")
)
})

test_that("git_remote_set_pushurl without add replaces push URL", {
Expand All @@ -20,11 +31,18 @@ test_that("git_remote_set_pushurl without add replaces push URL", {

git_remote_add("https://example.com/fetch", name = "origin", repo = repo)

git_remote_set_pushurl("https://example.com/push1", remote = "origin", repo = repo)
git_remote_set_pushurl("https://example.com/push2", remote = "origin", repo = repo)
git_remote_set_pushurl(
"https://example.com/push1",
remote = "origin",
repo = repo
)
git_remote_set_pushurl(
"https://example.com/push2",
remote = "origin",
repo = repo
)

cfg <- git_config(repo = repo)
pushurls <- cfg$value[cfg$name == "remote.origin.pushurl"]
pushurls <- git_config_get("remote.origin.pushurl", repo = repo)
expect_equal(pushurls, "https://example.com/push2")
})

Expand Down
Loading