#: * `update` [] #: #: Fetch the newest version of Homebrew and all formulae from GitHub using `git`(1) and perform any necessary migrations. #: #: --merge Use `git merge` to apply updates (rather than `git rebase`). #: --preinstall Run on auto-updates (e.g. before `brew install`). Skips some slower steps. #: -f, --force Always do a slower, full update check (even if unnecessary). #: -v, --verbose Print the directories checked and `git` operations performed. #: -d, --debug Display a trace of all shell commands as they are executed. #: -h, --help Show this message. # HOMEBREW_CURLRC, HOMEBREW_DEVELOPER, HOMEBREW_GIT_EMAIL, HOMEBREW_GIT_NAME # HOMEBREW_UPDATE_CLEANUP, HOMEBREW_UPDATE_TO_TAG are from the user environment # HOMEBREW_LIBRARY, HOMEBREW_PREFIX, HOMEBREW_REPOSITORY are set by bin/brew # HOMEBREW_BREW_DEFAULT_GIT_REMOTE, HOMEBREW_BREW_GIT_REMOTE, HOMEBREW_CACHE, HOMEBREW_CELLAR, HOMEBREW_CURL # HOMEBREW_DEV_CMD_RUN, HOMEBREW_FORCE_BREWED_CURL, HOMEBREW_FORCE_BREWED_GIT, HOMEBREW_SYSTEM_CURL_TOO_OLD # HOMEBREW_USER_AGENT_CURL are set by brew.sh # shellcheck disable=SC2154 source "${HOMEBREW_LIBRARY}/Homebrew/utils/lock.sh" # Replaces the function in Library/Homebrew/brew.sh to cache the Curl/Git executable to # provide speedup when using Curl/Git repeatedly (as update.sh does). curl() { if [[ -z "${CURL_EXECUTABLE}" ]] then CURL_EXECUTABLE="$("${HOMEBREW_LIBRARY}/Homebrew/shims/shared/curl" --homebrew=print-path)" fi "${CURL_EXECUTABLE}" "$@" } git() { if [[ -z "${GIT_EXECUTABLE}" ]] then GIT_EXECUTABLE="$("${HOMEBREW_LIBRARY}/Homebrew/shims/shared/git" --homebrew=print-path)" fi "${GIT_EXECUTABLE}" "$@" } git_init_if_necessary() { safe_cd "${HOMEBREW_REPOSITORY}" if [[ ! -d ".git" ]] then set -e trap '{ rm -rf .git; exit 1; }' EXIT git init git config --bool core.autocrlf false if [[ "${HOMEBREW_BREW_DEFAULT_GIT_REMOTE}" != "${HOMEBREW_BREW_GIT_REMOTE}" ]] then echo "HOMEBREW_BREW_GIT_REMOTE set: using ${HOMEBREW_BREW_GIT_REMOTE} for Homebrew/brew Git remote URL." fi git config remote.origin.url "${HOMEBREW_BREW_GIT_REMOTE}" git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" git fetch --force --tags origin git remote set-head origin --auto >/dev/null git reset --hard origin/master SKIP_FETCH_BREW_REPOSITORY=1 set +e trap - EXIT fi [[ -d "${HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-core" ]] || return safe_cd "${HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-core" if [[ ! -d ".git" ]] then set -e trap '{ rm -rf .git; exit 1; }' EXIT git init git config --bool core.autocrlf false if [[ "${HOMEBREW_CORE_DEFAULT_GIT_REMOTE}" != "${HOMEBREW_CORE_GIT_REMOTE}" ]] then echo "HOMEBREW_CORE_GIT_REMOTE set: using ${HOMEBREW_CORE_GIT_REMOTE} for Homebrew/core Git remote URL." fi git config remote.origin.url "${HOMEBREW_CORE_GIT_REMOTE}" git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" git fetch --force origin refs/heads/master:refs/remotes/origin/master git remote set-head origin --auto >/dev/null git reset --hard origin/master SKIP_FETCH_CORE_REPOSITORY=1 set +e trap - EXIT fi } repo_var() { local repo_var repo_var="$1" if [[ "${repo_var}" == "${HOMEBREW_REPOSITORY}" ]] then repo_var="" else repo_var="${repo_var#"${HOMEBREW_LIBRARY}/Taps"}" repo_var="$(echo -n "${repo_var}" | tr -C "A-Za-z0-9" "_" | tr "[:lower:]" "[:upper:]")" fi echo "${repo_var}" } upstream_branch() { local upstream_branch upstream_branch="$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null)" if [[ -z "${upstream_branch}" ]] then git remote set-head origin --auto >/dev/null upstream_branch="$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null)" fi upstream_branch="${upstream_branch#refs/remotes/origin/}" [[ -z "${upstream_branch}" ]] && upstream_branch="master" echo "${upstream_branch}" } read_current_revision() { git rev-parse -q --verify HEAD } pop_stash() { [[ -z "${STASHED}" ]] && return if [[ -n "${HOMEBREW_VERBOSE}" ]] then echo "Restoring your stashed changes to ${DIR}..." git stash pop else git stash pop "${QUIET_ARGS[@]}" 1>/dev/null fi unset STASHED } pop_stash_message() { [[ -z "${STASHED}" ]] && return echo "To restore the stashed changes to ${DIR}, run:" echo " cd ${DIR} && git stash pop" unset STASHED } reset_on_interrupt() { if [[ "${INITIAL_BRANCH}" != "${UPSTREAM_BRANCH}" && -n "${INITIAL_BRANCH}" ]] then git checkout "${INITIAL_BRANCH}" "${QUIET_ARGS[@]}" fi if [[ -n "${INITIAL_REVISION}" ]] then git rebase --abort &>/dev/null git merge --abort &>/dev/null git reset --hard "${INITIAL_REVISION}" "${QUIET_ARGS[@]}" fi if [[ -n "${HOMEBREW_NO_UPDATE_CLEANUP}" ]] then pop_stash else pop_stash_message fi exit 130 } # Used for testing purposes, e.g. for testing formula migration after # renaming it in the currently checked-out branch. To test run # "brew update --simulate-from-current-branch" simulate_from_current_branch() { local DIR local TAP_VAR local UPSTREAM_BRANCH local CURRENT_REVISION DIR="$1" cd "${DIR}" || return TAP_VAR="$2" UPSTREAM_BRANCH="$3" CURRENT_REVISION="$4" INITIAL_REVISION="$(git rev-parse -q --verify "${UPSTREAM_BRANCH}")" export HOMEBREW_UPDATE_BEFORE"${TAP_VAR}"="${INITIAL_REVISION}" export HOMEBREW_UPDATE_AFTER"${TAP_VAR}"="${CURRENT_REVISION}" if [[ "${INITIAL_REVISION}" != "${CURRENT_REVISION}" ]] then HOMEBREW_UPDATED="1" fi if ! git merge-base --is-ancestor "${INITIAL_REVISION}" "${CURRENT_REVISION}" then odie "Your ${DIR} HEAD is not a descendant of ${UPSTREAM_BRANCH}!" fi } merge_or_rebase() { if [[ -n "${HOMEBREW_VERBOSE}" ]] then echo "Updating ${DIR}..." fi local DIR local TAP_VAR local UPSTREAM_BRANCH DIR="$1" cd "${DIR}" || return TAP_VAR="$2" UPSTREAM_BRANCH="$3" unset STASHED trap reset_on_interrupt SIGINT if [[ "${DIR}" == "${HOMEBREW_REPOSITORY}" && -n "${HOMEBREW_UPDATE_TO_TAG}" ]] then UPSTREAM_TAG="$( git tag --list | sort --field-separator=. --key=1,1nr -k 2,2nr -k 3,3nr | grep --max-count=1 '^[0-9]*\.[0-9]*\.[0-9]*$' )" else UPSTREAM_TAG="" fi if [[ -n "${UPSTREAM_TAG}" ]] then REMOTE_REF="refs/tags/${UPSTREAM_TAG}" UPSTREAM_BRANCH="stable" else REMOTE_REF="origin/${UPSTREAM_BRANCH}" fi if [[ -n "$(git status --untracked-files=all --porcelain 2>/dev/null)" ]] then if [[ -n "${HOMEBREW_VERBOSE}" ]] then echo "Stashing uncommitted changes to ${DIR}..." fi git merge --abort &>/dev/null git rebase --abort &>/dev/null git reset --mixed "${QUIET_ARGS[@]}" if ! git -c "user.email=brew-update@localhost" \ -c "user.name=brew update" \ stash save --include-untracked "${QUIET_ARGS[@]}" then odie </dev/null then git checkout --force "${UPSTREAM_BRANCH}" "${QUIET_ARGS[@]}" else if [[ -n "${UPSTREAM_TAG}" && "${UPSTREAM_BRANCH}" != "master" ]] then git checkout --force -B "master" "origin/master" "${QUIET_ARGS[@]}" fi git checkout --force -B "${UPSTREAM_BRANCH}" "${REMOTE_REF}" "${QUIET_ARGS[@]}" fi fi INITIAL_REVISION="$(read_current_revision)" export HOMEBREW_UPDATE_BEFORE"${TAP_VAR}"="${INITIAL_REVISION}" # ensure we don't munge line endings on checkout git config core.autocrlf false if [[ -z "${HOMEBREW_MERGE}" ]] then # Work around bug where git rebase --quiet is not quiet if [[ -z "${HOMEBREW_VERBOSE}" ]] then git rebase "${QUIET_ARGS[@]}" "${REMOTE_REF}" >/dev/null else git rebase "${QUIET_ARGS[@]}" "${REMOTE_REF}" fi else git merge --no-edit --ff "${QUIET_ARGS[@]}" "${REMOTE_REF}" \ --strategy=recursive \ --strategy-option=ours \ --strategy-option=ignore-all-space fi CURRENT_REVISION="$(read_current_revision)" export HOMEBREW_UPDATE_AFTER"${TAP_VAR}"="${CURRENT_REVISION}" if [[ "${INITIAL_REVISION}" != "${CURRENT_REVISION}" ]] then HOMEBREW_UPDATED="1" fi trap '' SIGINT if [[ -n "${HOMEBREW_NO_UPDATE_CLEANUP}" ]] then if [[ "${INITIAL_BRANCH}" != "${UPSTREAM_BRANCH}" && -n "${INITIAL_BRANCH}" ]] && [[ ! "${INITIAL_BRANCH}" =~ ^v[0-9]+\.[0-9]+\.[0-9]|stable$ ]] then git checkout "${INITIAL_BRANCH}" "${QUIET_ARGS[@]}" fi pop_stash else pop_stash_message fi trap - SIGINT } homebrew-update() { local option local DIR local UPSTREAM_BRANCH for option in "$@" do case "${option}" in -\? | -h | --help | --usage) brew help update exit $? ;; --verbose) HOMEBREW_VERBOSE=1 ;; --debug) HOMEBREW_DEBUG=1 ;; --quiet) HOMEBREW_QUIET=1 ;; --merge) HOMEBREW_MERGE=1 ;; --force) HOMEBREW_UPDATE_FORCE=1 ;; --simulate-from-current-branch) HOMEBREW_SIMULATE_FROM_CURRENT_BRANCH=1 ;; --preinstall) export HOMEBREW_UPDATE_PREINSTALL=1 ;; --*) ;; -*) [[ "${option}" == *v* ]] && HOMEBREW_VERBOSE=1 [[ "${option}" == *q* ]] && HOMEBREW_QUIET=1 [[ "${option}" == *d* ]] && HOMEBREW_DEBUG=1 [[ "${option}" == *f* ]] && HOMEBREW_UPDATE_FORCE=1 ;; *) odie </dev/null || [[ -n "${HOMEBREW_FORCE_BREWED_GIT}" && ! -x "${HOMEBREW_PREFIX}/opt/git/bin/git" ]] then # we cannot install a Homebrew Git if homebrew/core is unavailable. if [[ ! -d "${HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-core" && -z "${HOMEBREW_INSTALL_FROM_API}" ]] || ! brew install git then odie "'git' must be installed and in your PATH!" fi fi [[ -f "${HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-core/.git/shallow" ]] && HOMEBREW_CORE_SHALLOW=1 [[ -f "${HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-cask/.git/shallow" ]] && HOMEBREW_CASK_SHALLOW=1 if [[ -n "${HOMEBREW_CORE_SHALLOW}" && -n "${HOMEBREW_CASK_SHALLOW}" ]] then SHALLOW_COMMAND_PHRASE="These commands" SHALLOW_REPO_PHRASE="repositories" else SHALLOW_COMMAND_PHRASE="This command" SHALLOW_REPO_PHRASE="repository" fi if [[ -n "${HOMEBREW_CORE_SHALLOW}" || -n "${HOMEBREW_CASK_SHALLOW}" ]] then odie </dev/null)" GITHUB_API_ACCEPT="application/vnd.github.v3+json" GITHUB_API_ENDPOINT="tags" else # Only try to `git fetch` when the upstream branch is at a different SHA # (so the API does not return 304: unmodified). GITHUB_API_ETAG="$(git rev-parse "refs/remotes/origin/${UPSTREAM_BRANCH_DIR}")" GITHUB_API_ACCEPT="application/vnd.github.v3.sha" GITHUB_API_ENDPOINT="commits/${UPSTREAM_BRANCH_DIR}" fi # HOMEBREW_CURL is set by brew.sh (and isn't mispelt here) # shellcheck disable=SC2153 UPSTREAM_SHA_HTTP_CODE="$( curl \ "${CURL_DISABLE_CURLRC_ARGS[@]}" \ --silent --max-time 3 \ --location --no-remote-time --output /dev/null --write-out "%{http_code}" \ --dump-header "${DIR}/.git/GITHUB_HEADERS" \ --user-agent "${HOMEBREW_USER_AGENT_CURL}" \ --header "Accept: ${GITHUB_API_ACCEPT}" \ --header "If-None-Match: \"${GITHUB_API_ETAG}\"" \ "https://api.github.com/repos/${UPSTREAM_REPOSITORY}/${GITHUB_API_ENDPOINT}" )" # Touch FETCH_HEAD to confirm we've checked for an update. [[ -f "${DIR}/.git/FETCH_HEAD" ]] && touch "${DIR}/.git/FETCH_HEAD" [[ -z "${HOMEBREW_UPDATE_FORCE}" ]] && [[ "${UPSTREAM_SHA_HTTP_CODE}" == "304" ]] && exit elif [[ -n "${HOMEBREW_UPDATE_PREINSTALL}" ]] then FORCE_AUTO_UPDATE="$(git config homebrew.forceautoupdate 2>/dev/null || echo "false")" if [[ "${FORCE_AUTO_UPDATE}" != "true" ]] then # Don't try to do a `git fetch` that may take longer than expected. exit fi fi # HOMEBREW_VERBOSE isn't modified here so ignore subshell warning. # shellcheck disable=SC2030 if [[ -n "${HOMEBREW_VERBOSE}" ]] then echo "Fetching ${DIR}..." fi local tmp_failure_file="${HOMEBREW_REPOSITORY}/.git/TMP_FETCH_FAILURES" rm -f "${tmp_failure_file}" if [[ -n "${HOMEBREW_UPDATE_PREINSTALL}" ]] then git fetch --tags --force "${QUIET_ARGS[@]}" origin \ "refs/heads/${UPSTREAM_BRANCH_DIR}:refs/remotes/origin/${UPSTREAM_BRANCH_DIR}" 2>/dev/null else # Capture stderr to tmp_failure_file if ! git fetch --tags --force "${QUIET_ARGS[@]}" origin \ "refs/heads/${UPSTREAM_BRANCH_DIR}:refs/remotes/origin/${UPSTREAM_BRANCH_DIR}" 2>>"${tmp_failure_file}" then # Reprint fetch errors to stderr [[ -f "${tmp_failure_file}" ]] && cat "${tmp_failure_file}" 1>&2 if [[ "${UPSTREAM_SHA_HTTP_CODE}" == "404" ]] then TAP="${DIR#${HOMEBREW_LIBRARY}/Taps/}" echo "${TAP} does not exist! Run \`brew untap ${TAP}\` to remove it." >>"${update_failed_file}" else echo "Fetching ${DIR} failed!" >>"${update_failed_file}" if [[ -f "${tmp_failure_file}" ]] && [[ "$(cat "${tmp_failure_file}")" == "fatal: couldn't find remote ref refs/heads/${UPSTREAM_BRANCH_DIR}" ]] then echo "${DIR}" >>"${missing_remote_ref_dirs_file}" fi fi fi fi rm -f "${tmp_failure_file}" ) & done wait trap - SIGINT if [[ -f "${update_failed_file}" ]] then onoe <"${update_failed_file}" rm -f "${update_failed_file}" export HOMEBREW_UPDATE_FAILED="1" fi if [[ -f "${missing_remote_ref_dirs_file}" ]] then HOMEBREW_MISSING_REMOTE_REF_DIRS="$(cat "${missing_remote_ref_dirs_file}")" rm -f "${missing_remote_ref_dirs_file}" export HOMEBREW_MISSING_REMOTE_REF_DIRS fi for DIR in "${HOMEBREW_REPOSITORY}" "${HOMEBREW_LIBRARY}"/Taps/*/* do # HOMEBREW_UPDATE_PREINSTALL wasn't modified in subshell. # shellcheck disable=SC2031 if [[ -n "${HOMEBREW_INSTALL_FROM_API}" ]] && [[ -n "${HOMEBREW_UPDATE_PREINSTALL}" ]] && [[ "${DIR}" == "${HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-core" || "${DIR}" == "${HOMEBREW_LIBRARY}/Taps/homebrew/homebrew-cask" ]] then continue fi [[ -d "${DIR}/.git" ]] || continue cd "${DIR}" || continue TAP_VAR="$(repo_var "${DIR}")" UPSTREAM_BRANCH_VAR="UPSTREAM_BRANCH${TAP_VAR}" UPSTREAM_BRANCH="${!UPSTREAM_BRANCH_VAR}" CURRENT_REVISION="$(read_current_revision)" PREFETCH_REVISION_VAR="PREFETCH_REVISION${TAP_VAR}" PREFETCH_REVISION="${!PREFETCH_REVISION_VAR}" POSTFETCH_REVISION="$(git rev-parse -q --verify refs/remotes/origin/"${UPSTREAM_BRANCH}")" # HOMEBREW_UPDATE_FORCE and HOMEBREW_VERBOSE weren't modified in subshell. # shellcheck disable=SC2031 if [[ -n "${HOMEBREW_SIMULATE_FROM_CURRENT_BRANCH}" ]] then simulate_from_current_branch "${DIR}" "${TAP_VAR}" "${UPSTREAM_BRANCH}" "${CURRENT_REVISION}" elif [[ -z "${HOMEBREW_UPDATE_FORCE}" ]] && [[ "${PREFETCH_REVISION}" == "${POSTFETCH_REVISION}" ]] && [[ "${CURRENT_REVISION}" == "${POSTFETCH_REVISION}" ]] then export HOMEBREW_UPDATE_BEFORE"${TAP_VAR}"="${CURRENT_REVISION}" export HOMEBREW_UPDATE_AFTER"${TAP_VAR}"="${CURRENT_REVISION}" else merge_or_rebase "${DIR}" "${TAP_VAR}" "${UPSTREAM_BRANCH}" [[ -n "${HOMEBREW_VERBOSE}" ]] && echo fi done safe_cd "${HOMEBREW_REPOSITORY}" # HOMEBREW_UPDATE_PREINSTALL wasn't modified in subshell. # shellcheck disable=SC2031 if [[ -n "${HOMEBREW_UPDATED}" ]] || [[ -n "${HOMEBREW_UPDATE_FAILED}" ]] || [[ -n "${HOMEBREW_MISSING_REMOTE_REF_DIRS}" ]] || [[ -n "${HOMEBREW_UPDATE_FORCE}" ]] || [[ -d "${HOMEBREW_LIBRARY}/LinkedKegs" ]] || [[ ! -f "${HOMEBREW_CACHE}/all_commands_list.txt" ]] || [[ -n "${HOMEBREW_DEVELOPER}" && -z "${HOMEBREW_UPDATE_PREINSTALL}" ]] then brew update-report "$@" return $? elif [[ -z "${HOMEBREW_UPDATE_PREINSTALL}" && -z "${HOMEBREW_QUIET}" ]] then echo "Already up-to-date." fi }