aboutsummaryrefslogtreecommitdiff
path: root/scripts/vim-patch.sh
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/vim-patch.sh')
-rwxr-xr-xscripts/vim-patch.sh226
1 files changed, 176 insertions, 50 deletions
diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh
index 2cc32f0dd0..9c4349abca 100755
--- a/scripts/vim-patch.sh
+++ b/scripts/vim-patch.sh
@@ -5,6 +5,12 @@ set -u
# Use privileged mode, which e.g. skips using CDPATH.
set -p
+# Ensure that the user has a bash that supports -A
+if [[ "${BASH_VERSINFO[0]}" -lt 4 ]]; then
+ >&2 echo "error: script requires bash 4+ (you have ${BASH_VERSION})."
+ exit 1
+fi
+
readonly NVIM_SOURCE_DIR="${NVIM_SOURCE_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
readonly VIM_SOURCE_DIR_DEFAULT="${NVIM_SOURCE_DIR}/.vim-src"
readonly VIM_SOURCE_DIR="${VIM_SOURCE_DIR:-${VIM_SOURCE_DIR_DEFAULT}}"
@@ -23,6 +29,7 @@ usage() {
echo " -h Show this message and exit."
echo " -l [git-log opts] List missing Vim patches."
echo " -L [git-log opts] List missing Vim patches (for scripts)."
+ echo " -m {vim-revision} List previous (older) missing Vim patches."
echo " -M List all merged patch-numbers (at current v:version)."
echo " -p {vim-revision} Download and generate a Vim patch. vim-revision"
echo " can be a Vim version (8.0.xxx) or a Git hash."
@@ -89,7 +96,7 @@ get_vim_sources() {
echo "Cloning Vim into: ${VIM_SOURCE_DIR}"
git clone https://github.com/vim/vim.git "${VIM_SOURCE_DIR}"
cd "${VIM_SOURCE_DIR}"
- else
+ elif [[ "${1-}" == update ]]; then
cd "${VIM_SOURCE_DIR}"
if ! [ -d ".git" ] \
&& ! [ "$(git rev-parse --show-toplevel)" = "${VIM_SOURCE_DIR}" ]; then
@@ -103,6 +110,8 @@ get_vim_sources() {
else
msg_err "Could not update Vim sources; ignoring error."
fi
+ else
+ cd "${VIM_SOURCE_DIR}"
fi
}
@@ -124,7 +133,7 @@ find_git_remote() {
}
# Assign variables for a given Vim tag, patch version, or commit.
-# Might exit in case it cannot be found.
+# Might exit in case it cannot be found, after updating Vim sources.
assign_commit_details() {
local vim_commit_ref
if [[ ${1} =~ v?[0-9]\.[0-9]\.[0-9]{3,4} ]]; then
@@ -146,9 +155,14 @@ assign_commit_details() {
local munge_commit_line=false
fi
- vim_commit=$(git -C "${VIM_SOURCE_DIR}" log -1 --format="%H" "${vim_commit_ref}" --) || {
- >&2 msg_err "Couldn't find Vim revision '${vim_commit_ref}'."
- exit 3
+ local get_vim_commit_cmd="git -C ${VIM_SOURCE_DIR} log -1 --format=%H ${vim_commit_ref} --"
+ vim_commit=$($get_vim_commit_cmd 2>&1) || {
+ # Update Vim sources.
+ get_vim_sources update
+ vim_commit=$($get_vim_commit_cmd 2>&1) || {
+ >&2 msg_err "Couldn't find Vim revision '${vim_commit_ref}': git error: ${vim_commit}."
+ exit 3
+ }
}
vim_commit_url="https://github.com/vim/vim/commit/${vim_commit}"
@@ -227,7 +241,8 @@ get_vimpatch() {
msg_ok "Saved patch to '${NVIM_SOURCE_DIR}/${patch_file}'."
}
-# shellcheck disable=SC2015 # "Note that A && B || C is not if-then-else."
+# shellcheck disable=SC2015
+# ^ "Note that A && B || C is not if-then-else."
stage_patch() {
get_vimpatch "$1"
local try_apply="${2:-}"
@@ -298,7 +313,8 @@ git_hub_pr() {
git hub pull new -m "$1"
}
-# shellcheck disable=SC2015 # "Note that A && B || C is not if-then-else."
+# shellcheck disable=SC2015
+# ^ "Note that A && B || C is not if-then-else."
submit_pr() {
require_executable git
local push_first
@@ -366,7 +382,7 @@ submit_pr() {
# Gets all Vim commits since the "start" commit.
list_vim_commits() { (
- cd "${VIM_SOURCE_DIR}" && git log --reverse --format='%H' v8.0.0000..HEAD "$@"
+ cd "${VIM_SOURCE_DIR}" && git log --reverse v8.0.0000..HEAD "$@"
) }
# Prints all (sorted) "vim-patch:xxx" tokens found in the Nvim git log.
@@ -388,19 +404,69 @@ list_vimpatch_numbers() {
done
}
+declare -A tokens
+declare -A vim_commit_tags
+
+_set_tokens_and_tags() {
+ set +u # Avoid "unbound variable" with bash < 4.4 below.
+ if [[ -n "${tokens[*]}" ]]; then
+ return
+ fi
+ set -u
+
+ # Find all "vim-patch:xxx" tokens in the Nvim git log.
+ for token in $(list_vimpatch_tokens); do
+ tokens[$token]=1
+ done
+
+ # Create an associative array mapping Vim commits to tags.
+ eval "vim_commit_tags=(
+ $(git -C "${VIM_SOURCE_DIR}" for-each-ref refs/tags \
+ --format '[%(objectname)]=%(refname:strip=2)' \
+ --sort='-*authordate' \
+ --shell)
+ )"
+ # Exit in case of errors from the above eval (empty vim_commit_tags).
+ if ! (( "${#vim_commit_tags[@]}" )); then
+ msg_err "Could not get Vim commits/tags."
+ exit 1
+ fi
+}
+
# Prints a newline-delimited list of Vim commits, for use by scripts.
+# "$1": use extended format? (with subject)
# "$@" is passed to list_vim_commits, as extra arguments to git-log.
list_missing_vimpatches() {
+ local -a missing_vim_patches=()
+ _set_missing_vimpatches "$@"
+ set +u # Avoid "unbound variable" with bash < 4.4 below.
+ for line in "${missing_vim_patches[@]}"; do
+ printf '%s\n' "$line"
+ done
+ set -u
+}
+
+# Sets / appends to missing_vim_patches (useful to avoid a subshell when
+# used multiple times to cache tokens/vim_commit_tags).
+# "$1": use extended format? (with subject)
+# "$@": extra arguments to git-log.
+_set_missing_vimpatches() {
local token vim_commit vim_tag patch_number
- declare -A tokens
- declare -A vim_commit_tags
declare -a git_log_args
+ local extended_format=$1; shift
+ if [[ "$extended_format" == 1 ]]; then
+ git_log_args=("--format=%H %s")
+ else
+ git_log_args=("--format=%H")
+ fi
+
# Massage arguments for git-log.
declare -A git_log_replacements=(
[^\(.*/\)?src/nvim/\(.*\)]="\${BASH_REMATCH[1]}src/\${BASH_REMATCH[2]}"
[^\(.*/\)?\.vim-src/\(.*\)]="\${BASH_REMATCH[2]}"
)
+ local i j
for i in "$@"; do
for j in "${!git_log_replacements[@]}"; do
if [[ "$i" =~ $j ]]; then
@@ -411,33 +477,31 @@ list_missing_vimpatches() {
git_log_args+=("$i")
done
- # Find all "vim-patch:xxx" tokens in the Nvim git log.
- for token in $(list_vimpatch_tokens); do
- tokens[$token]=1
- done
-
- # Create an associative array mapping Vim commits to tags.
- eval "declare -A vim_commit_tags=(
- $(git -C "${VIM_SOURCE_DIR}" for-each-ref refs/tags \
- --format '[%(objectname)]=%(refname:strip=2)' \
- --sort='-*authordate' \
- --shell)
- )"
- # Exit in case of errors from the above eval (empty vim_commit_tags).
- if ! (( "${#vim_commit_tags[@]}" )); then
- msg_err "Could not get Vim commits/tags."
- exit 1
- fi
+ _set_tokens_and_tags
# Get missing Vim commits
set +u # Avoid "unbound variable" with bash < 4.4 below.
- for vim_commit in $(list_vim_commits "${git_log_args[@]}"); do
+ local vim_commit info
+ while IFS=' ' read -r line; do
# Check for vim-patch:<commit_hash> (usually runtime updates).
- token="vim-patch:${vim_commit:0:7}"
+ token="vim-patch:${line:0:7}"
if [[ "${tokens[$token]-}" ]]; then
continue
fi
+ # Get commit hash, and optional info from line. This is used in
+ # extended mode, and when using e.g. '--format' manually.
+ vim_commit=${line%% *}
+ if [[ "$vim_commit" == "$line" ]]; then
+ info=
+ else
+ info=${line#* }
+ if [[ -n $info ]]; then
+ # Remove any "patch 8.0.0902: " prefixes, and prefix with ": ".
+ info=": ${info#patch*: }"
+ fi
+ fi
+
vim_tag="${vim_commit_tags[$vim_commit]-}"
if [[ -n "$vim_tag" ]]; then
# Check for vim-patch:<tag> (not commit hash).
@@ -445,26 +509,26 @@ list_missing_vimpatches() {
if [[ "${tokens[$patch_number]-}" ]]; then
continue
fi
- echo "$vim_tag"
+ missing_vim_patches+=("$vim_tag$info")
else
- echo "$vim_commit"
+ missing_vim_patches+=("$vim_commit$info")
fi
- done
+ done < <(list_vim_commits "${git_log_args[@]}")
set -u
}
# Prints a human-formatted list of Vim commits, with instructional messages.
# Passes "$@" onto list_missing_vimpatches (args for git-log).
show_vimpatches() {
- get_vim_sources
- printf "\nVim patches missing from Neovim:\n"
+ get_vim_sources update
+ printf "Vim patches missing from Neovim:\n"
local -A runtime_commits
for commit in $(git -C "${VIM_SOURCE_DIR}" log --format="%H %D" -- runtime | sed 's/,\? tag: / /g'); do
runtime_commits[$commit]=1
done
- list_missing_vimpatches "$@" | while read -r vim_commit; do
+ list_missing_vimpatches 1 "$@" | while read -r vim_commit; do
if [[ "${runtime_commits[$vim_commit]-}" ]]; then
printf ' • %s (+runtime)\n' "${vim_commit}"
else
@@ -472,18 +536,73 @@ show_vimpatches() {
fi
done
- printf "Instructions:
+ cat << EOF
- To port one of the above patches to Neovim, execute
- this script with the patch revision as argument and
- follow the instructions.
-
- Examples: '%s -p 7.4.487'
- '%s -p 1e8ebf870720e7b671f98f22d653009826304c4f'
+Instructions:
+ To port one of the above patches to Neovim, execute this script with the patch revision as argument and follow the instructions, e.g.
+ '${BASENAME} -p v8.0.1234', or '${BASENAME} -P v8.0.1234'
NOTE: Please port the _oldest_ patch if you possibly can.
- Out-of-order patches increase the possibility of bugs.
-" "${BASENAME}" "${BASENAME}"
+ You can use '${BASENAME} -l path/to/file' to see what patches are missing for a file.
+EOF
+}
+
+list_missing_previous_vimpatches_for_patch() {
+ local for_vim_patch="${1}"
+ local vim_commit vim_tag
+ assign_commit_details "${for_vim_patch}"
+
+ local file
+ local -a missing_list
+ local -a fnames
+ while IFS= read -r line ; do
+ fnames+=("$line")
+ done < <(git -C "${VIM_SOURCE_DIR}" diff-tree --no-commit-id --name-only -r "${vim_commit}")
+ local i=0
+ local n=${#fnames[@]}
+ printf '=== getting missing patches for %d files ===\n' "$n"
+ if [[ -z "${vim_tag}" ]]; then
+ printf 'NOTE: "%s" is not a Vim tag - listing all oldest missing patches\n' "${for_vim_patch}" >&2
+ fi
+ for fname in "${fnames[@]}"; do
+ i=$(( i+1 ))
+ printf '[%.*d/%d] %s: ' "${#n}" "$i" "$n" "$fname"
+
+ local -a missing_vim_patches=()
+ _set_missing_vimpatches 1 -- "${fname}"
+
+ set +u # Avoid "unbound variable" with bash < 4.4 below.
+ local missing_vim_commit_info="${missing_vim_patches[0]}"
+ if [[ -z "${missing_vim_commit_info}" ]]; then
+ printf -- "-\n"
+ else
+ local missing_vim_commit="${missing_vim_commit_info%%:*}"
+ if [[ -z "${vim_tag}" ]] || [[ "${missing_vim_commit}" < "${vim_tag}" ]]; then
+ printf -- "%s\n" "$missing_vim_commit_info"
+ missing_list+=("$missing_vim_commit_info")
+ else
+ printf -- "-\n"
+ fi
+ fi
+ set -u
+ done
+
+ set +u # Avoid "unbound variable" with bash < 4.4 below.
+ if [[ -z "${missing_list[*]}" ]]; then
+ msg_ok 'no missing previous Vim patches'
+ set -u
+ return 0
+ fi
+ set -u
+
+ local -a missing_unique
+ while IFS= read -r line; do
+ missing_unique+=("$line")
+ done < <(printf '%s\n' "${missing_list[@]}" | sort -u)
+
+ msg_err "$(printf '%d missing previous Vim patches:' ${#missing_unique[@]})"
+ printf ' - %s\n' "${missing_unique[@]}"
+ return 1
}
review_commit() {
@@ -561,9 +680,11 @@ review_pr() {
echo
echo "Downloading data for pull request #${pr}."
- local pr_commit_urls=(
- "$(curl -Ssf "https://api.github.com/repos/neovim/neovim/pulls/${pr}/commits" \
- | jq -r '.[].html_url')")
+ local -a pr_commit_urls
+ while IFS= read -r pr_commit_url; do
+ pr_commit_urls+=("$pr_commit_url")
+ done < <(curl -Ssf "https://api.github.com/repos/neovim/neovim/pulls/${pr}/commits" \
+ | jq -r '.[].html_url')
echo "Found ${#pr_commit_urls[@]} commit(s)."
@@ -583,7 +704,7 @@ review_pr() {
clean_files
}
-while getopts "hlLMVp:P:g:r:s" opt; do
+while getopts "hlLmMVp:P:g:r:s" opt; do
case ${opt} in
h)
usage
@@ -596,13 +717,18 @@ while getopts "hlLMVp:P:g:r:s" opt; do
;;
L)
shift # remove opt
- list_missing_vimpatches "$@"
+ list_missing_vimpatches 0 "$@"
exit 0
;;
M)
list_vimpatch_numbers
exit 0
;;
+ m)
+ shift # remove opt
+ list_missing_previous_vimpatches_for_patch "$@"
+ exit 0
+ ;;
p)
stage_patch "${OPTARG}"
exit
@@ -624,7 +750,7 @@ while getopts "hlLMVp:P:g:r:s" opt; do
exit 0
;;
V)
- get_vim_sources
+ get_vim_sources update
exit 0
;;
*)