Skip to content

Commit fd73bfa

Browse files
authored
Merge branch 'main' into restore
2 parents 86c461a + 4b1279d commit fd73bfa

12 files changed

Lines changed: 198 additions & 56 deletions

File tree

NAMESPACE

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@ export(git_commit_id)
2828
export(git_commit_info)
2929
export(git_commit_stats)
3030
export(git_config)
31+
export(git_config_get)
3132
export(git_config_global)
33+
export(git_config_global_get)
3234
export(git_config_global_set)
35+
export(git_config_local_get)
3336
export(git_config_set)
3437
export(git_conflicts)
3538
export(git_diff)

NEWS.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# gert (development version)
22

3-
- Add `git_restore()` function (#259)
3+
- Add `git_restore()` function (#259)
4+
- 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).
5+
- `git_clone()` without a `path` argument now clones into a directory named after the
6+
"humanish" part of the URL, so "git@github.com:francisbarton/myrepo.git" gets cloned into `myrepo` (@francisbardon, #192).
47
- `git_remote_set_pushurl()` gains an `add` argument to append push URLs instead of replacing them. (@robitalec, #128)
58
- Fix `git_info()` for the case when no upstream is configured (@mpage, #263)
69
- `git_branch_create()`: `force` now also applies to the checkout step, allowing branch creation even when local changes would be overwritten (@MichaelChirico, #177).

R/config.R

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44
#' Get or set Git options, as `git config` does on the command line. **Global**
55
#' settings affect all of a user's Git operations (`git config --global`),
66
#' whereas **local** settings are scoped to a specific repository (`git config
7-
#' --local`). When both exist, local options always win. Four functions address
8-
#' the four possible combinations of getting vs setting and global vs. local.
7+
#' --local`). When both exist, local options always win.
98
#'
109
#' ```{r echo = FALSE, results = "asis"}
1110
#' dat <- data.frame(
12-
#' local = c("`git_config()`", "`git_config_set()`"),
13-
#' global = c("`git_config_global()`", "`git_config_global_set()`"),
14-
#' row.names = c("get", "set")
11+
#' local = c("`git_config()`", "`git_config_get()`", "`git_config_local_get()`", "`git_config_set()`"),
12+
#' global = c("`git_config_global()`", "`git_config_get()`", "`git_config_global_get()`", "`git_config_global_set()`"),
13+
#' row.names = c("get all", "get one (local+global)", "get one (local or global only)", "set")
1514
#' )
1615
#' knitr::kable(dat, col.names = paste0("**", colnames(dat), "**"))
1716
#' ```
@@ -21,6 +20,11 @@
2120
#' option is determined from global or local config.
2221
#' * `git_config_global()`: a `data.frame`, as for `git_config()`, except only
2322
#' for global Git options.
23+
#' * `git_config_get()`: the value of the named option considering both local and
24+
#' global config (local wins), or `NULL` if unset.
25+
#' * `git_config_local_get()`: as for `git_config_get()`, but restricted to
26+
#' local (repository-level) config only.
27+
#' * `git_config_global_get()`: as for `git_config_get()`, but for global config only.
2428
#' * `git_config_set()`, `git_config_global_set()`: The previous value(s) of
2529
#' `name` in local or global config, respectively. If this option was
2630
#' previously unset, returns `NULL`. Returns invisibly.
@@ -36,15 +40,16 @@
3640
#'
3741
#' previous <- git_config_set("aaa.bbb", "ccc", repo = r)
3842
#' previous
39-
#' cfg <- git_config(repo = r)
40-
#' subset(cfg, level == "local")
41-
#' cfg$value[cfg$name == "aaa.bbb"]
43+
#' git_config_local_get("aaa.bbb", repo = r)
4244
#'
4345
#' previous <- git_config_set("aaa.bbb", NULL, repo = r)
4446
#' previous
45-
#' cfg <- git_config(repo = r)
46-
#' subset(cfg, level == "local")
47-
#' cfg$value[cfg$name == "aaa.bbb"]
47+
#' git_config_local_get("aaa.bbb", repo = r)
48+
#'
49+
#' # Get a single named option (returns NULL if unset)
50+
#' git_config_get("aaa.bbb", repo = r)
51+
#' git_config_set("aaa.bbb", "ccc", repo = r)
52+
#' git_config_get("aaa.bbb", repo = r)
4853
#'
4954
#' unlink(r, recursive = TRUE)
5055
#'
@@ -53,6 +58,10 @@
5358
#' git_config_global_set("user.name", "Your Name")
5459
#' git_config_global_set("user.email", "your@email.com")
5560
#' git_config_global()
61+
#'
62+
#' # Get a single global option (returns NULL if unset)
63+
#' git_config_global_get("user.name")
64+
#' git_config_global_get("gert.nonexistent")
5665
#' }
5766
#' @export
5867
#' @family git
@@ -70,10 +79,41 @@ git_config_global <- function() {
7079
.Call(R_git_config_list, NULL)
7180
}
7281

82+
#' @export
83+
#' @rdname git_config
84+
#' @param name Name of the option to get or set
85+
git_config_get <- function(name, repo = '.') {
86+
cfg <- git_config(repo = repo)
87+
if (!name %in% cfg$name) {
88+
return(NULL)
89+
}
90+
cfg$value[cfg$name == name]
91+
}
92+
93+
#' @export
94+
#' @rdname git_config
95+
git_config_local_get <- function(name, repo = '.') {
96+
cfg <- git_config(repo = repo)
97+
cfg <- cfg[cfg$level == "local", ]
98+
if (!name %in% cfg$name) {
99+
return(NULL)
100+
}
101+
cfg$value[cfg$name == name]
102+
}
103+
104+
#' @export
105+
#' @rdname git_config
106+
git_config_global_get <- function(name) {
107+
cfg <- git_config_global()
108+
if (!name %in% cfg$name) {
109+
return(NULL)
110+
}
111+
cfg$value[cfg$name == name]
112+
}
113+
73114
#' @export
74115
#' @rdname git_config
75116
#' @useDynLib gert R_git_config_set
76-
#' @param name Name of the option to set
77117
#' @param value Value to set. Must be a string, logical, number or `NULL` (to
78118
#' unset).
79119
#' @param add if `TRUE`, append a new entry for `name` instead of replacing
@@ -85,8 +125,7 @@ git_config_set <- function(name, value, add = FALSE, repo = '.') {
85125
}
86126
repo <- git_open(repo)
87127
name <- as.character(name)
88-
orig_cfg <- git_config(repo = repo)
89-
out <- orig_cfg$value[orig_cfg$name == name & orig_cfg$level == "local"]
128+
out <- git_config_local_get(name, repo = repo)
90129
.Call(R_git_config_set, repo, name, value, add)
91130
if (length(out) > 0) {
92131
invisible(out)
@@ -98,8 +137,7 @@ git_config_set <- function(name, value, add = FALSE, repo = '.') {
98137
#' @export
99138
#' @rdname git_config
100139
git_config_global_set <- function(name, value, add = FALSE) {
101-
orig_cfg <- git_config_global()
102-
out <- orig_cfg$value[orig_cfg$name == name]
140+
out <- git_config_global_get(name)
103141
.Call(R_git_config_set, NULL, name, value, add)
104142
if (length(out) > 0) {
105143
invisible(out)
@@ -141,9 +179,8 @@ configure_global_user <- function() {
141179
}
142180

143181
global_user_is_configured <- function() {
144-
cfg <- git_config_global()
145-
user_name_exists <- any(cfg$name == "user.name")
146-
user_email_exists <- any(cfg$name == "user.email")
182+
user_name_exists <- !is.null(git_config_global_get("user.name"))
183+
user_email_exists <- !is.null(git_config_global_get("user.email"))
147184
user_name_exists && user_email_exists
148185
}
149186

@@ -161,14 +198,10 @@ global_user_is_configured <- function() {
161198
#' `FALSE` otherwise.
162199
#'
163200
#' @export
164-
#' @examples
201+
#' @examplesIf interactive()
165202
#' user_is_configured()
166203
user_is_configured <- function(repo = ".") {
167-
cfg <- tryCatch(
168-
git_config(repo),
169-
error = function(e) git_config_global()
170-
)
171-
user_name_exists <- any(cfg$name == "user.name")
172-
user_email_exists <- any(cfg$name == "user.email")
204+
user_name_exists <- !is.null(git_config_get("user.name", repo = repo))
205+
user_email_exists <- !is.null(git_config_get("user.email", repo = repo))
173206
user_name_exists && user_email_exists
174207
}

R/fetch.R

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ git_push <- function(
174174
#' repositories, and `https://yourname@github.com/` or `git@github.com/` for
175175
#' private repos. You will be prompted for a password or pat when needed.
176176
#' @param path Directory of the Git repository to create.
177+
#' By default, the "humanish" part of the URL.
178+
#' For instance, "git@github.com:someone/myrepo.git" will be cloned to
179+
#' `myrepo/`.
177180
#' @param ssh_key path or object containing your ssh private key. By default we
178181
#' look for keys in `ssh-agent` and [credentials::ssh_key_info()].
179182
#' @param branch name of branch to check out locally
@@ -220,8 +223,13 @@ git_clone <- function(
220223
verbose = interactive()
221224
) {
222225
stopifnot(is.character(url))
226+
# "humanish" part of the URL
227+
# https://github.com/git/git/blob/6e8d538aab8fe4dd07ba9fb87b5c7edcfa5706ad/dir.h#L494
223228
if (!length(path)) {
224-
path <- file.path(getwd(), basename(url))
229+
path <- file.path(
230+
getwd(),
231+
sub("\\.git$", "", sub("/.git$", "", basename(url)))
232+
)
225233
}
226234
stopifnot(is.character(path))
227235
stopifnot(is.null(branch) || is.character(branch))
@@ -249,7 +257,7 @@ git_clone <- function(
249257
#' @param rebase if TRUE we try to rebase instead of merge local changes. This
250258
#' is not possible in case of conflicts (you will get an error).
251259
#' @param ... arguments passed to `git_fetch()`
252-
git_pull <- function(remote = NULL, rebase = FALSE, ..., repo = '.'){
260+
git_pull <- function(remote = NULL, rebase = FALSE, ..., repo = '.') {
253261
repo <- git_open(repo)
254262
info <- git_info(repo)
255263
branch <- info$shorthand

R/repository.R

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#' * `git_info()` shows basic information about a repository, such as the SHA
66
#' and branch of the current HEAD.
77
#'
8+
#' @section Path:
9+
#'
810
#' For `git_init()` the `path` parameter sets the directory of the git repository
911
#' to create. If this directory already exists, it must be empty. If it does
1012
#' not exist, it is created, along with any intermediate directories that don't
@@ -21,7 +23,7 @@
2123
#' @family git
2224
#' @useDynLib gert R_git_repository_init
2325
#' @inheritParams git_open
24-
#' @param path the location of the git repository, see details.
26+
#' @param path the location of the git repository, see the "path" section.
2527
#' @param bare if true, a Git repository without a working directory is created
2628
#' @git repository
2729
#' @return
@@ -68,6 +70,9 @@ git_find <- function(path = '.') {
6870
#' @export
6971
#' @rdname git_repo
7072
#' @useDynLib gert R_git_repository_info
73+
#' @section Detached head:
74+
#' If `git_info()$shorthand` is equal to `HEAD`,
75+
#' it means the repository is in a [detached head state](https://jvns.ca/blog/2023/11/01/confusing-git-terminology/#detached-head-state).
7176
git_info <- function(repo = '.') {
7277
repo <- git_open(repo)
7378
.Call(R_git_repository_info, repo)

man/git_config.Rd

Lines changed: 30 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/git_fetch.Rd

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/git_repo.Rd

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/user_is_configured.Rd

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)