R/checkout.R

Defines functions get_default_branch checkout_default_branch file_path has_uncommited_changes check_checkout checkout_repo checkout

Documented in checkout

#' Checkout Git repositories
#'
#' Checkout the `main` or `master` branch of the given Git repositories. But
#' stay on the current reference (e.g. branch) if checking out the repository
#' that called `checkout()`.
#'
#' @param repos Path to one or more Git repositories.
#'
#' @return Called for its side effect. Returns `repos` invisibly.
#' @export
#' @examples
#' library(magrittr)
#'
#' # Helper
#' walk <- function(x, f, ...) {
#'   lapply(x, f, ...)
#'   invisible(x)
#' }
#'
#' # Setup two minimal repositories.
#' repos <- file.path(tempdir(), paste0("repo", 1:2))
#' repos
#' repos %>% walk(dir.create)
#' repos %>%
#'   file.path("a-file.txt") %>%
#'   walk(file.create)
#' repos %>%
#'   walk_git("init") %>%
#'   walk_git("config user.name Jerry") %>%
#'   walk_git("config user.email jerry@gmail.com") %>%
#'   walk_git("add .") %>%
#'   walk_git("commit -m 'New file'")
#'
#' # If we set the directory at `repo1`, it stays at the branch `pr`, whereas the
#' # `repo2` changes to the branch `master` (or `main`).
#'
#' oldwd <- getwd()
#' setwd(repos[[1]])
#'
#' repos %>% walk_git("checkout -b pr")
#'
#' # Compare before and after `checkout()`
#' repos %>% walk_git("branch", verbose = TRUE)
#' repos %>% checkout()
#' repos %>% walk_git("branch", verbose = TRUE)
#'
#' # Cleanup
#' setwd(oldwd)
#' repos %>% walk(unlink, recursive = TRUE)
checkout <- function(repos) {
  unlist(lapply(repos, checkout_repo))
  invisible(repos)
}

checkout_repo <- function(repo) {
  check_checkout(repo)

  if (file_path(repo) == file_path(getwd())) {
    return(invisible(repo))
  } else {
    checkout_default_branch(repo)
  }

  invisible(repo)
}

check_checkout <- function(repo) {
  stopifnot(length(repo) == 1)


  if (is_git_error(walk_git(repo, "status"))) {
    stop("`repo` must be a git repository. Did you forget to initialize it?")
  }


  if (has_uncommited_changes(repo)) {
    stop("`repo` must not have uncommited changes: ", repo, call. = FALSE)
  }

  invisible(repo)
}

has_uncommited_changes <- function(repo) {
  status <- map_git(repo, "status")
  clean <- any(grepl("nothing to commit", status))
  !clean
}

file_path <- function(path) {
  remake_path <- function(x) file.path(dirname(x), basename(x))
  unlist(lapply(path, remake_path))
}

checkout_default_branch <- function(repo) {
  branches <- system(git_command(repo, "branch"), intern = TRUE)
  checkout_default <- sprintf("checkout %s", get_default_branch(branches))
  walk_git(repo, checkout_default)

  invisible(repo)
}

get_default_branch <- function(x) {
  choices <- "^[*] main$|^[ ] main$|^[*] master$|^[ ] master$"

  out <- grep(choices, x, value = TRUE)
  out <- gsub("[* ]", "", out)
  out
}
maurolepore/checkout documentation built on Jan. 12, 2021, 1:27 p.m.