2014-12-25 12:50:28 -07:00
#!/usr/bin/env bash
set -e
2015-07-22 08:00:17 -07:00
set -u
2016-08-14 05:19:46 -07:00
# Use privileged mode, which e.g. skips using CDPATH.
2016-08-11 05:09:50 -07:00
set -p
2014-11-21 06:39:35 -07:00
2020-04-02 07:56:14 -07:00
# Ensure that the user has a bash that supports -A
2020-04-07 00:31:26 -07:00
if [ [ " ${ BASH_VERSINFO [0] } " -lt 4 ] ] ; then
2020-05-31 10:27:05 -07:00
>& 2 echo " error: script requires bash 4+ (you have ${ BASH_VERSION } ). "
2020-04-02 07:56:14 -07:00
exit 1
fi
2016-09-12 04:40:55 -07:00
readonly NVIM_SOURCE_DIR = " ${ NVIM_SOURCE_DIR :- $( cd " $( dirname " ${ BASH_SOURCE [0] } " ) /.. " && pwd ) } "
readonly VIM_SOURCE_DIR_DEFAULT = " ${ NVIM_SOURCE_DIR } /.vim-src "
2015-03-05 19:49:03 -07:00
readonly VIM_SOURCE_DIR = " ${ VIM_SOURCE_DIR :- ${ VIM_SOURCE_DIR_DEFAULT } } "
2021-07-01 09:37:03 -07:00
BASENAME = " $( basename " ${ 0 } " ) "
readonly BASENAME
2016-02-08 16:05:49 -07:00
readonly BRANCH_PREFIX = "vim-"
2014-11-21 06:39:35 -07:00
2016-01-23 04:05:04 -07:00
CREATED_FILES = ( )
2015-01-07 12:52:10 -07:00
usage( ) {
2017-12-25 19:38:42 -07:00
echo "Port Vim patches to Neovim"
2015-03-05 19:49:03 -07:00
echo "https://github.com/neovim/neovim/wiki/Merging-patches-from-upstream-vim"
echo
2015-07-22 08:00:17 -07:00
echo " Usage: ${ BASENAME } [-h | -l | -p vim-revision | -r pr-number] "
2015-03-05 19:49:03 -07:00
echo
echo "Options:"
2015-07-22 08:00:17 -07:00
echo " -h Show this message and exit."
2019-07-29 22:45:59 -07:00
echo " -l [git-log opts] List missing Vim patches."
echo " -L [git-log opts] List missing Vim patches (for scripts)."
2020-01-26 06:50:25 -07:00
echo " -m {vim-revision} List previous (older) missing Vim patches."
2017-12-25 19:38:42 -07:00
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."
echo " -P {vim-revision} Download, generate and apply a Vim patch."
echo " -g {vim-revision} Download a Vim patch."
echo " -s Create a vim-patch pull request."
echo " -r {pr-number} Review a vim-patch pull request."
2019-06-08 05:02:30 -07:00
echo " -V Clone the Vim source code to \$VIM_SOURCE_DIR."
2015-03-05 19:49:03 -07:00
echo
2019-06-08 05:02:30 -07:00
echo " \$VIM_SOURCE_DIR controls where Vim sources are found"
2017-12-10 17:58:55 -07:00
echo " (default: ' ${ VIM_SOURCE_DIR_DEFAULT } ') "
2019-07-29 22:45:59 -07:00
echo
echo "Examples:"
echo
echo " - List missing patches for a given file (in the Vim source):"
echo " $0 -l -- src/edit.c "
2015-01-07 12:52:10 -07:00
}
2018-03-08 04:58:16 -07:00
msg_ok( ) {
2018-06-19 06:27:32 -07:00
printf '\e[32m✔\e[0m %s\n' " $@ "
2018-03-08 04:58:16 -07:00
}
msg_err( ) {
2019-06-09 07:13:06 -07:00
printf '\e[31m✘\e[0m %s\n' " $@ " >& 2
2018-03-08 04:58:16 -07:00
}
2015-07-22 08:00:17 -07:00
# Checks if a program is in the user's PATH, and is executable.
check_executable( ) {
2016-05-16 20:41:41 -07:00
test -x " $( command -v " ${ 1 } " ) "
}
require_executable( ) {
if ! check_executable " ${ 1 } " ; then
2015-07-22 08:00:17 -07:00
>& 2 echo " ${ BASENAME } : ' ${ 1 } ' not found in PATH or not executable. "
exit 1
fi
}
2016-01-23 04:05:04 -07:00
clean_files( ) {
if [ [ ${# CREATED_FILES [@] } -eq 0 ] ] ; then
return
fi
echo
echo "Created files:"
local file
2016-05-11 15:09:39 -07:00
for file in " ${ CREATED_FILES [@] } " ; do
2016-01-23 04:05:04 -07:00
echo " • ${ file } "
done
read -p "Delete these files (Y/n)? " -n 1 -r reply
echo
2016-12-21 19:17:01 -07:00
if [ [ " ${ reply } " = = n ] ] ; then
2016-01-23 04:05:04 -07:00
echo "You can use 'git clean' to remove these files when you're done."
2016-12-21 19:17:01 -07:00
else
rm -- " ${ CREATED_FILES [@] } "
2016-01-23 04:05:04 -07:00
fi
}
2015-01-07 12:52:10 -07:00
get_vim_sources( ) {
2016-05-16 20:41:41 -07:00
require_executable git
2015-07-22 08:00:17 -07:00
2015-01-07 12:52:10 -07:00
if [ [ ! -d ${ VIM_SOURCE_DIR } ] ] ; then
2018-03-08 04:58:16 -07:00
echo " Cloning Vim into: ${ VIM_SOURCE_DIR } "
2016-04-08 10:54:50 -07:00
git clone https://github.com/vim/vim.git " ${ VIM_SOURCE_DIR } "
2016-01-08 17:12:36 -07:00
cd " ${ VIM_SOURCE_DIR } "
2019-10-16 06:44:38 -07:00
elif [ [ " ${ 1 - } " = = update ] ] ; then
2018-08-20 08:41:00 -07:00
cd " ${ VIM_SOURCE_DIR } "
2018-08-20 23:37:49 -07:00
if ! [ -d ".git" ] \
&& ! [ " $( git rev-parse --show-toplevel) " = " ${ VIM_SOURCE_DIR } " ] ; then
2018-03-08 04:58:16 -07:00
msg_err " ${ VIM_SOURCE_DIR } does not appear to be a git repository. "
2015-08-18 21:21:11 -07:00
echo " Please remove it and try again."
exit 1
fi
2018-03-08 04:58:16 -07:00
echo " Updating Vim sources: ${ VIM_SOURCE_DIR } "
2019-06-08 05:02:30 -07:00
if git pull --ff; then
msg_ok "Updated Vim sources."
else
2018-03-08 04:58:16 -07:00
msg_err "Could not update Vim sources; ignoring error."
2019-06-08 05:02:30 -07:00
fi
2019-10-16 06:44:38 -07:00
else
cd " ${ VIM_SOURCE_DIR } "
2015-01-07 12:52:10 -07:00
fi
}
2015-07-22 08:00:17 -07:00
commit_message( ) {
2017-12-16 19:42:08 -07:00
if [ [ -n " $vim_tag " ] ] ; then
2018-03-24 14:42:13 -07:00
printf '%s\n%s' " ${ vim_message } " " ${ vim_commit_url } "
2017-12-16 19:42:08 -07:00
else
2018-03-24 14:42:13 -07:00
printf 'vim-patch:%s\n\n%s\n%s' " $vim_version " " $vim_message " " $vim_commit_url "
2017-12-16 19:42:08 -07:00
fi
2015-07-22 08:00:17 -07:00
}
2016-04-21 18:56:20 -07:00
find_git_remote( ) {
2019-07-29 22:45:59 -07:00
git_remote = $( git remote -v \
| awk '$2 ~ /github.com[:\/]neovim\/neovim/ && $3 == "(fetch)" {print $1; exit}' )
if [ [ -z " $git_remote " ] ] ; then
git_remote = "origin"
fi
echo " $git_remote "
2016-04-21 18:56:20 -07:00
}
2019-07-29 22:45:59 -07:00
# Assign variables for a given Vim tag, patch version, or commit.
2019-10-16 06:44:38 -07:00
# Might exit in case it cannot be found, after updating Vim sources.
2015-07-22 08:00:17 -07:00
assign_commit_details( ) {
2019-07-29 22:45:59 -07:00
local vim_commit_ref
if [ [ ${ 1 } = ~ v?[ 0-9] \. [ 0-9] \. [ 0-9] { 3,4} ] ] ; then
2015-08-25 23:07:31 -07:00
# Interpret parameter as version number (tag).
2019-07-29 22:45:59 -07:00
if [ [ " ${ 1 : 0 : 1 } " = = v ] ] ; then
vim_version = " ${ 1 : 1 } "
vim_tag = " ${ 1 } "
else
vim_version = " ${ 1 } "
vim_tag = " v ${ 1 } "
fi
vim_commit_ref = " $vim_tag "
2017-12-16 19:42:08 -07:00
local munge_commit_line = true
2015-01-07 12:52:10 -07:00
else
# Interpret parameter as commit hash.
2017-03-21 03:28:13 -07:00
vim_version = " ${ 1 : 0 : 12 } "
2017-12-16 19:42:08 -07:00
vim_tag =
2019-07-29 22:45:59 -07:00
vim_commit_ref = " $vim_version "
2017-12-16 19:42:08 -07:00
local munge_commit_line = false
2015-01-07 12:52:10 -07:00
fi
2015-08-25 23:07:31 -07:00
2019-10-16 06:44:38 -07:00
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
}
2019-07-29 22:45:59 -07:00
}
2015-08-25 23:07:31 -07:00
vim_commit_url = " https://github.com/vim/vim/commit/ ${ vim_commit } "
2019-07-29 22:45:59 -07:00
vim_message = " $( git -C " ${ VIM_SOURCE_DIR } " log -1 --pretty= 'format:%B' " ${ vim_commit } " \
2018-08-09 12:36:05 -07:00
| sed -e 's/\(#[0-9]\{1,\}\)/vim\/vim\1/g' ) "
2017-12-16 19:42:08 -07:00
if [ [ ${ munge_commit_line } = = "true" ] ] ; then
2015-07-22 08:00:17 -07:00
# Remove first line of commit message.
2017-12-16 19:42:08 -07:00
vim_message = " $( echo " ${ vim_message } " | sed -e '1s/^patch /vim-patch:/' ) "
2015-07-22 08:00:17 -07:00
fi
2016-01-23 13:45:21 -07:00
patch_file = " vim- ${ vim_version } .patch "
2015-07-22 08:00:17 -07:00
}
2016-10-19 06:43:04 -07:00
# Patch surgery
2016-09-12 04:40:55 -07:00
preprocess_patch( ) {
local file = " $1 "
2016-09-13 03:18:07 -07:00
local nvim = "nvim -u NORC -i NONE --headless"
2016-09-12 04:40:55 -07:00
2021-02-03 16:53:27 -07:00
# Remove Filelist, README
local na_files = 'Filelist\|README.*'
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/\<\%(' " ${ na_files } " '\)\>@norm! d/\v(^diff)|%$
' +w +q " $file "
# Remove *.proto, Make*, INSTALL*, gui_*, beval.*, some if_*, gvim, libvterm, tee, VisVim, xpm, xxd
local na_src = 'auto\|configure.*\|GvimExt\|libvterm\|proto\|tee\|VisVim\|xpm\|xxd\|Make*\|INSTALL*\|beval.*\|gui_*\|if_lua\|if_mzsch\|if_olepp\|if_ole\|if_perl\|if_py\|if_ruby\|if_tcl\|if_xcmdsrv'
2019-06-08 05:02:30 -07:00
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\S*\<\%(testdir/\)\@<!\%(' " ${ na_src } " '\)@norm! d/\v(^diff)|%$
' +w +q " $file "
2016-09-12 04:40:55 -07:00
2018-11-04 14:59:39 -07:00
# Remove unwanted Vim doc files.
local na_doc = 'channel\.txt\|netbeans\.txt\|os_\w\+\.txt\|term\.txt\|todo\.txt\|version\d\.txt\|sponsor\.txt\|intro\.txt\|tags'
2019-06-08 05:02:30 -07:00
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/runtime/doc/\<\%(' " ${ na_doc } " '\)\>@norm! d/\v(^diff)|%$
' +w +q " $file "
2016-09-12 04:40:55 -07:00
2017-03-21 03:28:13 -07:00
# Remove "Last change ..." changes in doc files.
2>/dev/null $nvim --cmd 'set dir=/tmp' +'%s/^@@.*\n.*For Vim version.*Last change.*\n.*For Vim version.*Last change.*//' +w +q " $file "
2021-02-03 16:53:27 -07:00
# Remove gui, option, setup, screen dumps, testdir/Make_*.mak files
local na_src_testdir = 'gen_opt_test.vim\|gui_.*\|Make_amiga.mak\|Make_dos.mak\|Make_ming.mak\|Make_vms.mms\|dumps/.*.dump\|setup_gui.vim'
2019-06-08 05:02:30 -07:00
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/testdir/\<\%(' " ${ na_src_testdir } " '\)\>@norm! d/\v(^diff)|%$
' +w +q " $file "
2016-09-12 04:40:55 -07:00
2021-02-03 16:53:27 -07:00
# Remove testdir/test_*.vim files
local na_src_testdir = 'balloon.*\|channel.*\|crypt.vim\|gui.*\|job_fails.vim\|json.vim\|mzscheme.vim\|netbeans.*\|paste.vim\|popupwin.*\|restricted.vim\|shortpathname.vim\|tcl.vim\|terminal.*\|xxd.vim'
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/testdir/\<test_\%(' " ${ na_src_testdir } " '\)\>@norm! d/\v(^diff)|%$
' +w +q " $file "
2017-12-10 16:54:25 -07:00
# Remove version.c #7555
local na_po = 'version.c'
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\<\%(' ${ na_po } '\)\>@norm! d/\v(^diff)|%$
' +w +q " $file "
2016-11-17 09:27:13 -07:00
# Remove some *.po files. #5622
local na_po = 'sjiscorr.c\|ja.sjis.po\|ko.po\|pl.cp1250.po\|pl.po\|ru.cp1251.po\|uk.cp1251.po\|zh_CN.cp936.po\|zh_CN.po\|zh_TW.po'
2017-04-19 12:26:01 -07:00
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/po/\<\%(' ${ na_po } '\)\>@norm! d/\v(^diff)|%$
' +w +q " $file "
2017-11-07 12:25:34 -07:00
# Remove vimrc_example.vim
local na_vimrcexample = 'vimrc_example\.vim'
2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/runtime/\<\%(' ${ na_vimrcexample } '\)\>@norm! d/\v(^diff)|%$
' +w +q " $file "
2016-11-17 09:27:13 -07:00
2016-09-12 04:40:55 -07:00
# Rename src/ paths to src/nvim/
2016-10-19 06:43:04 -07:00
LC_ALL = C sed -e 's/\( [ab]\/src\)/\1\/nvim/g' \
2018-10-13 12:05:07 -07:00
" $file " > " $file " .tmp && mv " $file " .tmp " $file "
2018-10-13 09:41:44 -07:00
2020-11-01 11:18:29 -07:00
# Rename evalfunc.c to eval/funcs.c
LC_ALL = C sed -e 's/\( [ab]\/src\/nvim\)\/evalfunc\.c/\1\/eval\/funcs\.c/g' \
" $file " > " $file " .tmp && mv " $file " .tmp " $file "
2020-11-01 11:22:52 -07:00
# Rename userfunc.c to eval/userfunc.c
LC_ALL = C sed -e 's/\( [ab]\/src\/nvim\)\/userfunc\.c/\1\/eval\/userfunc\.c/g' \
" $file " > " $file " .tmp && mv " $file " .tmp " $file "
2020-11-01 11:18:29 -07:00
# Rename session.c to ex_session.c
2020-11-01 10:13:33 -07:00
LC_ALL = C sed -e 's/\( [ab]\/src\/nvim\)\/session\(\.[ch]\)/\1\/ex_session\2/g' \
" $file " > " $file " .tmp && mv " $file " .tmp " $file "
2018-10-13 09:41:44 -07:00
# Rename test_urls.vim to check_urls.vim
LC_ALL = C sed -e 's@\( [ab]\)/runtime/doc/test\(_urls.vim\)@\1/scripts/check\2@g' \
" $file " > " $file " .tmp && mv " $file " .tmp " $file "
2018-10-13 12:02:57 -07:00
# Rename path to check_colors.vim
LC_ALL = C sed -e 's@\( [ab]/runtime\)/colors/\(tools/check_colors.vim\)@\1/\2@g' \
" $file " > " $file " .tmp && mv " $file " .tmp " $file "
2016-09-12 04:40:55 -07:00
}
2017-12-16 13:38:53 -07:00
get_vimpatch( ) {
2015-07-22 08:00:17 -07:00
get_vim_sources
assign_commit_details " ${ 1 } "
2015-01-07 12:52:10 -07:00
2018-03-08 04:58:16 -07:00
msg_ok " Found Vim revision ' ${ vim_commit } '. "
2015-01-07 12:52:10 -07:00
2016-09-12 04:40:55 -07:00
local patch_content
2019-06-08 05:42:17 -07:00
patch_content = " $( git --no-pager show --unified= 5 --color= never -1 --pretty= medium " ${ vim_commit } " ) "
2015-01-07 12:52:10 -07:00
2016-09-12 04:40:55 -07:00
cd " ${ NVIM_SOURCE_DIR } "
2016-12-12 18:48:55 -07:00
printf "Creating patch...\n"
echo " $patch_content " > " ${ NVIM_SOURCE_DIR } / ${ patch_file } "
printf "Pre-processing patch...\n"
preprocess_patch " ${ NVIM_SOURCE_DIR } / ${ patch_file } "
2018-08-20 08:41:00 -07:00
msg_ok " Saved patch to ' ${ NVIM_SOURCE_DIR } / ${ patch_file } '. "
2016-12-12 18:48:55 -07:00
}
2020-01-26 01:24:42 -07:00
# shellcheck disable=SC2015
# ^ "Note that A && B || C is not if-then-else."
2016-12-12 18:48:55 -07:00
stage_patch( ) {
2017-12-16 13:38:53 -07:00
get_vimpatch " $1 "
2017-11-06 16:29:19 -07:00
local try_apply = " ${ 2 :- } "
2016-12-12 18:48:55 -07:00
2016-05-11 15:09:39 -07:00
local git_remote
git_remote = " $( find_git_remote) "
local checked_out_branch
checked_out_branch = " $( git rev-parse --abbrev-ref HEAD) "
2016-04-21 18:56:20 -07:00
2016-02-08 16:05:49 -07:00
if [ [ " ${ checked_out_branch } " = = ${ BRANCH_PREFIX } * ] ] ; then
2018-03-08 04:58:16 -07:00
msg_ok " Current branch ' ${ checked_out_branch } ' seems to be a vim-patch "
2016-02-08 16:05:49 -07:00
echo " branch; not creating a new branch."
else
2018-06-19 06:27:32 -07:00
printf '\nFetching "%s/master".\n' " ${ git_remote } "
2016-04-21 18:56:20 -07:00
output = " $( git fetch " ${ git_remote } " master 2>& 1) " &&
2018-03-08 04:58:16 -07:00
msg_ok " ${ output } " ||
( msg_err " ${ output } " ; false )
2016-02-08 16:05:49 -07:00
2016-12-12 18:48:55 -07:00
local nvim_branch = " ${ BRANCH_PREFIX } ${ vim_version } "
2016-02-08 16:05:49 -07:00
echo
2016-09-13 03:18:07 -07:00
echo " Creating new branch ' ${ nvim_branch } ' based on ' ${ git_remote } /master'. "
2016-09-12 04:40:55 -07:00
cd " ${ NVIM_SOURCE_DIR } "
2016-09-13 03:18:07 -07:00
output = " $( git checkout -b " ${ nvim_branch } " " ${ git_remote } /master " 2>& 1) " &&
2018-03-08 04:58:16 -07:00
msg_ok " ${ output } " ||
( msg_err " ${ output } " ; false )
2016-02-08 16:05:49 -07:00
fi
2016-09-13 03:18:07 -07:00
printf "\nCreating empty commit with correct commit message.\n"
2016-02-08 16:05:49 -07:00
output = " $( commit_message | git commit --allow-empty --file 2>& 1 -) " &&
2018-03-08 04:58:16 -07:00
msg_ok " ${ output } " ||
( msg_err " ${ output } " ; false )
2015-01-07 12:52:10 -07:00
2019-06-08 05:42:17 -07:00
local ret = 0
2017-11-06 16:29:19 -07:00
if test -n " $try_apply " ; then
if ! check_executable patch; then
2018-03-08 04:58:16 -07:00
printf "\n"
msg_err "'patch' command not found\n"
2017-11-06 16:29:19 -07:00
else
printf "\nApplying patch...\n"
2019-06-08 06:06:16 -07:00
patch -p1 --fuzz= 1 --no-backup-if-mismatch < " ${ patch_file } " || ret = $?
2017-11-06 16:29:19 -07:00
fi
printf "\nInstructions:\n Proceed to port the patch.\n"
else
2018-06-19 06:27:32 -07:00
printf '\nInstructions:\n Proceed to port the patch.\n Try the "patch" command (or use "%s -P ..." next time):\n patch -p1 < %s\n' " ${ BASENAME } " " ${ patch_file } "
2017-11-06 16:29:19 -07:00
fi
2016-09-13 03:18:07 -07:00
2018-06-19 06:27:32 -07:00
printf '
Stage your changes ( "git add ..." ) , then use "git commit --amend" to commit.
2016-09-13 03:18:07 -07:00
2018-06-19 06:27:32 -07:00
To port more patches ( if any) related to %s,
run "%s" again.
2016-09-13 03:18:07 -07:00
* Do this only for _related_ patches ( otherwise it increases the
size of the pull request, making it harder to review)
2018-06-19 06:27:32 -07:00
When you are done , try "%s -s" to create the pull request.
2016-09-13 03:18:07 -07:00
See the wiki for more information:
2018-06-19 06:27:32 -07:00
* https://github.com/neovim/neovim/wiki/Merging-patches-from-upstream-vim
' " ${ vim_version } " " ${ BASENAME } " " ${ BASENAME } "
2019-06-08 05:42:17 -07:00
return $ret
2015-01-07 12:52:10 -07:00
}
2016-05-16 20:53:56 -07:00
hub_pr( ) {
hub pull-request -m " $1 "
}
git_hub_pr( ) {
git hub pull new -m " $1 "
}
2020-01-26 01:24:42 -07:00
# shellcheck disable=SC2015
# ^ "Note that A && B || C is not if-then-else."
2016-01-23 13:45:21 -07:00
submit_pr( ) {
2016-05-16 20:41:41 -07:00
require_executable git
2016-05-16 20:53:56 -07:00
local push_first
push_first = 1
local submit_fn
if check_executable hub; then
submit_fn = "hub_pr"
elif check_executable git-hub; then
push_first = 0
submit_fn = "git_hub_pr"
else
>& 2 echo " ${ BASENAME } : 'hub' or 'git-hub' not found in PATH or not executable. "
2019-03-01 17:24:27 -07:00
>& 2 echo " Get it here: https://hub.github.com/"
2016-05-16 20:53:56 -07:00
exit 1
fi
2016-01-23 13:45:21 -07:00
2016-09-12 04:40:55 -07:00
cd " ${ NVIM_SOURCE_DIR } "
2016-05-11 15:09:39 -07:00
local checked_out_branch
checked_out_branch = " $( git rev-parse --abbrev-ref HEAD) "
2016-02-08 16:05:49 -07:00
if [ [ " ${ checked_out_branch } " != ${ BRANCH_PREFIX } * ] ] ; then
2018-03-08 04:58:16 -07:00
msg_err " Current branch ' ${ checked_out_branch } ' doesn't seem to be a vim-patch branch. "
2016-01-23 13:45:21 -07:00
exit 1
fi
2016-05-11 15:09:39 -07:00
local git_remote
git_remote = " $( find_git_remote) "
local pr_body
2017-12-16 19:42:08 -07:00
pr_body = " $( git log --grep= vim-patch --reverse --format= '#### %s%n%n%b%n' " ${ git_remote } " /master..HEAD) "
2016-05-11 15:09:39 -07:00
local patches
2017-12-16 19:42:08 -07:00
# Extract just the "vim-patch:X.Y.ZZZZ" or "vim-patch:sha" portion of each log
patches = ( " $( git log --grep= vim-patch --reverse --format= '%s' " ${ git_remote } " /master..HEAD | sed 's/: .*//' ) " )
2020-09-04 05:46:46 -07:00
# shellcheck disable=SC2206
patches = ( ${ patches [@]//vim-patch : } ) # Remove 'vim-patch:' prefix for each item in array.
2016-05-11 15:09:39 -07:00
local pr_title = " ${ patches [*] } " # Create space-separated string from array.
2016-02-08 16:05:49 -07:00
pr_title = " ${ pr_title // /, } " # Replace spaces with commas.
2016-01-23 13:45:21 -07:00
2016-05-11 15:09:39 -07:00
local pr_message
2021-06-28 17:40:00 -07:00
pr_message = " $( printf 'vim-patch:%s\n\n%s\n' " ${ pr_title #, } " " ${ pr_body } " ) "
2016-01-23 13:45:21 -07:00
2016-05-16 20:53:56 -07:00
if [ [ $push_first -ne 0 ] ] ; then
echo " Pushing to 'origin/ ${ checked_out_branch } '. "
output = " $( git push origin " ${ checked_out_branch } " 2>& 1) " &&
2018-03-08 04:58:16 -07:00
msg_ok " ${ output } " ||
( msg_err " ${ output } " ; false )
2016-05-16 20:53:56 -07:00
echo
fi
2016-01-23 13:45:21 -07:00
echo "Creating pull request."
2016-05-16 20:53:56 -07:00
output = " $( ${ submit_fn } " ${ pr_message } " 2>& 1) " &&
2018-03-08 04:58:16 -07:00
msg_ok " ${ output } " ||
( msg_err " ${ output } " ; false )
2016-01-23 13:45:21 -07:00
echo
echo "Cleaning up files."
2016-02-08 16:05:49 -07:00
local patch_file
2016-05-11 15:09:39 -07:00
for patch_file in " ${ patches [@] } " ; do
2016-02-08 16:05:49 -07:00
patch_file = " vim- ${ patch_file } .patch "
2016-09-12 04:40:55 -07:00
if [ [ ! -f " ${ NVIM_SOURCE_DIR } / ${ patch_file } " ] ] ; then
2016-02-08 16:05:49 -07:00
continue
fi
2016-09-12 04:40:55 -07:00
rm -- " ${ NVIM_SOURCE_DIR } / ${ patch_file } "
2018-03-08 04:58:16 -07:00
msg_ok " Removed ' ${ NVIM_SOURCE_DIR } / ${ patch_file } '. "
2016-02-08 16:05:49 -07:00
done
2016-01-23 13:45:21 -07:00
}
2017-12-16 13:38:53 -07:00
# Gets all Vim commits since the "start" commit.
list_vim_commits( ) { (
2019-10-13 14:09:09 -07:00
cd " ${ VIM_SOURCE_DIR } " && git log --reverse v8.0.0000..HEAD " $@ "
2017-12-16 13:38:53 -07:00
) }
2015-01-07 12:52:10 -07:00
2017-12-25 19:38:42 -07:00
# Prints all (sorted) "vim-patch:xxx" tokens found in the Nvim git log.
2017-12-16 13:38:53 -07:00
list_vimpatch_tokens( ) {
2019-04-11 15:26:52 -07:00
# Use sed…{7,7} to normalize (internal) Git hashes (for tokens caches).
git -C " ${ NVIM_SOURCE_DIR } " log -E --grep= 'vim-patch:[^ ,{]{7,}' \
| grep -oE 'vim-patch:[^ ,{:]{7,}' \
2017-12-16 13:38:53 -07:00
| sort \
2019-04-11 15:26:52 -07:00
| uniq \
| sed -nE 's/^(vim-patch:([0-9]+\.[^ ]+|[0-9a-z]{7,7})).*/\1/p'
2017-12-16 13:38:53 -07:00
}
2017-12-25 19:38:42 -07:00
# Prints all patch-numbers (for the current v:version) for which there is
# a "vim-patch:xxx" token in the Nvim git log.
list_vimpatch_numbers( ) {
# Transform "vim-patch:X.Y.ZZZZ" to "ZZZZ".
2019-06-08 05:02:30 -07:00
list_vimpatch_tokens | while read -r vimpatch_token; do
2017-12-25 19:38:42 -07:00
echo " $vimpatch_token " | grep '8\.0\.' | sed 's/.*vim-patch:8\.0\.\([0-9a-z]\+\).*/\1/'
done
}
2019-12-24 00:15:18 -07:00
declare -A tokens
declare -A vim_commit_tags
_set_tokens_and_tags( ) {
2019-12-25 05:02:23 -07:00
set +u # Avoid "unbound variable" with bash < 4.4 below.
2019-12-24 00:15:18 -07:00
if [ [ -n " ${ tokens [*] } " ] ] ; then
return
fi
2019-12-25 05:02:23 -07:00
set -u
2019-12-24 00:15:18 -07:00
# 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
}
2017-12-16 13:38:53 -07:00
# Prints a newline-delimited list of Vim commits, for use by scripts.
2020-01-26 07:20:51 -07:00
# "$1": use extended format? (with subject)
2019-07-29 22:45:59 -07:00
# "$@" is passed to list_vim_commits, as extra arguments to git-log.
2017-12-16 13:38:53 -07:00
list_missing_vimpatches( ) {
2019-12-24 00:15:18 -07:00
local -a missing_vim_patches = ( )
_set_missing_vimpatches " $@ "
2019-12-27 22:11:38 -07:00
set +u # Avoid "unbound variable" with bash < 4.4 below.
2019-12-24 00:15:18 -07:00
for line in " ${ missing_vim_patches [@] } " ; do
printf '%s\n' " $line "
done
2019-12-27 22:11:38 -07:00
set -u
2019-12-24 00:15:18 -07:00
}
# Sets / appends to missing_vim_patches (useful to avoid a subshell when
# used multiple times to cache tokens/vim_commit_tags).
2020-01-26 07:20:51 -07:00
# "$1": use extended format? (with subject)
# "$@": extra arguments to git-log.
2019-12-24 00:15:18 -07:00
_set_missing_vimpatches( ) {
2019-04-11 15:26:52 -07:00
local token vim_commit vim_tag patch_number
2019-08-30 07:46:29 -07:00
declare -a git_log_args
2019-10-13 14:09:09 -07:00
local extended_format = $1 ; shift
if [ [ " $extended_format " = = 1 ] ] ; then
git_log_args = ( "--format=%H %s" )
else
git_log_args = ( "--format=%H" )
fi
2019-08-30 07:46:29 -07:00
# Massage arguments for git-log.
declare -A git_log_replacements = (
[ ^\( .*/\) ?src/nvim/\( .*\) ] = "\${BASH_REMATCH[1]}src/\${BASH_REMATCH[2]}"
[ ^\( .*/\) ?\. vim-src/\( .*\) ] = "\${BASH_REMATCH[2]}"
)
2019-12-24 00:15:18 -07:00
local i j
2019-08-30 07:46:29 -07:00
for i in " $@ " ; do
for j in " ${ !git_log_replacements[@] } " ; do
if [ [ " $i " = ~ $j ] ] ; then
eval " git_log_args+=( ${ git_log_replacements [ $j ] } ) "
continue 2
fi
done
git_log_args += ( " $i " )
done
2017-12-16 13:38:53 -07:00
2019-12-24 00:15:18 -07:00
_set_tokens_and_tags
2019-06-08 05:18:35 -07:00
2017-12-16 13:38:53 -07:00
# Get missing Vim commits
2019-09-03 01:12:02 -07:00
set +u # Avoid "unbound variable" with bash < 4.4 below.
2019-10-13 14:09:09 -07:00
local vim_commit info
while IFS = ' ' read -r line; do
2017-12-16 13:38:53 -07:00
# Check for vim-patch:<commit_hash> (usually runtime updates).
2019-10-13 14:09:09 -07:00
token = " vim-patch: ${ line : 0 : 7 } "
2019-04-11 15:26:52 -07:00
if [ [ " ${ tokens [ $token ]- } " ] ] ; then
continue
2015-01-07 12:52:10 -07:00
fi
2019-10-13 14:09:09 -07:00
# 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
2019-06-08 05:18:35 -07:00
vim_tag = " ${ vim_commit_tags [ $vim_commit ]- } "
if [ [ -n " $vim_tag " ] ] ; then
2019-04-11 15:26:52 -07:00
# Check for vim-patch:<tag> (not commit hash).
patch_number = " vim-patch: ${ vim_tag : 1 } " # "v7.4.0001" => "7.4.0001"
if [ [ " ${ tokens [ $patch_number ]- } " ] ] ; then
continue
fi
2019-12-24 00:15:18 -07:00
missing_vim_patches += ( " $vim_tag $info " )
2019-04-11 15:26:52 -07:00
else
2019-12-24 00:15:18 -07:00
missing_vim_patches += ( " $vim_commit $info " )
2017-12-10 16:49:44 -07:00
fi
2019-10-13 14:09:09 -07:00
done < <( list_vim_commits " ${ git_log_args [@] } " )
2019-09-03 01:12:02 -07:00
set -u
2017-12-10 16:49:44 -07:00
}
# Prints a human-formatted list of Vim commits, with instructional messages.
2019-08-30 07:46:29 -07:00
# Passes "$@" onto list_missing_vimpatches (args for git-log).
2017-12-16 13:38:53 -07:00
show_vimpatches( ) {
2019-10-16 06:44:38 -07:00
get_vim_sources update
printf "Vim patches missing from Neovim:\n"
2017-12-10 16:49:44 -07:00
2019-06-09 07:01:19 -07:00
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
2019-10-13 14:09:09 -07:00
list_missing_vimpatches 1 " $@ " | while read -r vim_commit; do
2019-06-09 07:01:19 -07:00
if [ [ " ${ runtime_commits [ $vim_commit ]- } " ] ] ; then
2018-06-19 06:27:32 -07:00
printf ' • %s (+runtime)\n' " ${ vim_commit } "
2017-12-10 16:49:44 -07:00
else
2018-06-19 06:27:32 -07:00
printf ' • %s\n' " ${ vim_commit } "
2015-01-07 12:52:10 -07:00
fi
done
2019-11-08 12:47:58 -07:00
cat << EOF
Instructions:
2019-10-16 06:44:38 -07:00
To port one of the above patches to Neovim, execute this script with the patch revision as argument and follow the instructions, e.g.
2019-11-08 12:47:58 -07:00
'${BASENAME} -p v8.0.1234' , or '${BASENAME} -P v8.0.1234'
2018-06-19 06:27:32 -07:00
NOTE: Please port the _oldest_ patch if you possibly can.
2019-11-08 12:47:58 -07:00
You can use '${BASENAME} -l path/to/file' to see what patches are missing for a file.
EOF
2015-01-07 12:52:10 -07:00
}
2019-12-24 00:15:18 -07:00
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 " )
2021-05-11 18:47:43 -07:00
done < <( git -C " ${ VIM_SOURCE_DIR } " diff-tree --no-commit-id --name-only -r " ${ vim_commit } " -- . ':!src/version.c' )
2019-12-24 00:15:18 -07:00
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 } "
2020-01-19 08:05:23 -07:00
set +u # Avoid "unbound variable" with bash < 4.4 below.
2021-05-11 18:28:54 -07:00
for missing_vim_commit_info in " ${ missing_vim_patches [@] } " ; do
if [ [ -z " ${ missing_vim_commit_info } " ] ] ; then
printf -- "-\r"
2019-12-24 00:15:18 -07:00
else
2021-05-11 18:28:54 -07:00
printf -- "-\r"
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 -- "-\r"
fi
2019-12-24 00:15:18 -07:00
fi
2021-05-11 18:28:54 -07:00
done
2020-01-19 08:05:23 -07:00
set -u
2019-12-24 00:15:18 -07:00
done
2020-01-11 13:09:54 -07:00
set +u # Avoid "unbound variable" with bash < 4.4 below.
2019-12-24 00:15:18 -07:00
if [ [ -z " ${ missing_list [*] } " ] ] ; then
msg_ok 'no missing previous Vim patches'
2020-01-26 07:07:41 -07:00
set -u
2019-12-24 00:15:18 -07:00
return 0
fi
2020-01-11 13:09:54 -07:00
set -u
2019-12-24 00:15:18 -07:00
local -a missing_unique
2020-10-15 18:52:44 -07:00
local stat
2019-12-24 00:15:18 -07:00
while IFS = read -r line; do
2020-01-26 07:21:33 -07:00
local commit = " ${ line %% : * } "
2020-10-15 18:52:44 -07:00
stat = " $( git -C " ${ VIM_SOURCE_DIR } " show --format= --shortstat " ${ commit } " ) "
2020-01-26 07:21:33 -07:00
missing_unique += ( " $( printf '%s\n %s' " $line " " $stat " ) " )
2019-12-24 00:15:18 -07:00
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
}
2016-01-23 04:05:04 -07:00
review_commit( ) {
2016-09-13 03:18:07 -07:00
local nvim_commit_url = " ${ 1 } "
local nvim_patch_url = " ${ nvim_commit_url } .patch "
2015-07-22 08:00:17 -07:00
local git_patch_prefix = 'Subject: \[PATCH\] '
2016-09-13 03:18:07 -07:00
local nvim_patch
nvim_patch = " $( curl -Ssf " ${ nvim_patch_url } " ) "
2016-05-11 15:09:39 -07:00
local vim_version
2018-07-09 10:55:18 -07:00
vim_version = " $( head -n 4 <<< " ${ nvim_patch } " | sed -n 's/' " ${ git_patch_prefix } " 'vim-patch:\([a-z0-9.]*\)\(:.*\)\{0,1\}$/\1/p' ) "
2015-07-22 08:00:17 -07:00
2016-01-23 04:05:04 -07:00
echo
2015-07-22 08:00:17 -07:00
if [ [ -n " ${ vim_version } " ] ] ; then
2018-03-08 04:58:16 -07:00
msg_ok " Detected Vim patch ' ${ vim_version } '. "
2015-07-22 08:00:17 -07:00
else
2018-03-08 04:58:16 -07:00
msg_err "Could not detect the Vim patch number."
2016-02-08 16:05:49 -07:00
echo " This script assumes that the PR contains only commits"
echo " with 'vim-patch:XXX' in their title."
2016-12-15 07:09:52 -07:00
echo
2018-06-19 06:27:32 -07:00
printf -- '%s\n\n' " $( head -n 4 <<< " ${ nvim_patch } " ) "
2016-12-15 07:09:52 -07:00
local reply
read -p "Continue reviewing (y/N)? " -n 1 -r reply
2016-12-21 19:17:01 -07:00
if [ [ " ${ reply } " = = y ] ] ; then
2016-12-15 07:09:52 -07:00
echo
return
fi
2015-07-22 08:00:17 -07:00
exit 1
fi
assign_commit_details " ${ vim_version } "
2018-07-10 16:54:44 -07:00
echo
echo "Creating files."
echo " ${ nvim_patch } " > " ${ NVIM_SOURCE_DIR } /n ${ patch_file } "
msg_ok " Saved pull request diff to ' ${ NVIM_SOURCE_DIR } /n ${ patch_file } '. "
CREATED_FILES += ( " ${ NVIM_SOURCE_DIR } /n ${ patch_file } " )
local nvim = "nvim -u NORC -n -i NONE --headless"
2>/dev/null $nvim --cmd 'set dir=/tmp' +'1,/^$/g/^ /-1join' +w +q " ${ NVIM_SOURCE_DIR } /n ${ patch_file } "
2016-05-11 15:09:39 -07:00
local expected_commit_message
expected_commit_message = " $( commit_message) "
local message_length
message_length = " $( wc -l <<< " ${ expected_commit_message } " ) "
local commit_message
2018-07-10 16:54:44 -07:00
commit_message = " $( tail -n +4 " ${ NVIM_SOURCE_DIR } /n ${ patch_file } " | head -n " ${ message_length } " ) "
2015-07-22 08:00:17 -07:00
if [ [ " ${ commit_message # ${ git_patch_prefix } } " = = " ${ expected_commit_message } " ] ] ; then
2018-03-08 04:58:16 -07:00
msg_ok "Found expected commit message."
2015-07-22 08:00:17 -07:00
else
2018-03-08 04:58:16 -07:00
msg_err "Wrong commit message."
2015-07-22 08:00:17 -07:00
echo " Expected:"
echo " ${ expected_commit_message } "
echo " Actual:"
echo " ${ commit_message # ${ git_patch_prefix } } "
fi
2017-12-16 13:38:53 -07:00
get_vimpatch " ${ vim_version } "
2016-09-12 04:40:55 -07:00
CREATED_FILES += ( " ${ NVIM_SOURCE_DIR } / ${ patch_file } " )
2015-07-22 08:00:17 -07:00
echo
echo "Launching nvim."
2016-09-12 04:40:55 -07:00
nvim -c " cd ${ NVIM_SOURCE_DIR } " \
-O " ${ NVIM_SOURCE_DIR } / ${ patch_file } " " ${ NVIM_SOURCE_DIR } /n ${ patch_file } "
2016-01-23 04:05:04 -07:00
}
review_pr( ) {
2016-05-16 20:41:41 -07:00
require_executable curl
require_executable nvim
require_executable jq
2016-01-23 04:05:04 -07:00
get_vim_sources
local pr = " ${ 1 } "
echo
echo " Downloading data for pull request # ${ pr } . "
2020-03-31 19:40:33 -07:00
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' )
2016-01-23 04:05:04 -07:00
echo " Found ${# pr_commit_urls [@] } commit(s). "
local pr_commit_url
local reply
2016-05-11 15:09:39 -07:00
for pr_commit_url in " ${ pr_commit_urls [@] } " ; do
2016-01-23 13:45:21 -07:00
review_commit " ${ pr_commit_url } "
2016-01-23 04:05:04 -07:00
if [ [ " ${ pr_commit_url } " != " ${ pr_commit_urls [-1] } " ] ] ; then
read -p "Continue with next commit (Y/n)? " -n 1 -r reply
echo
2016-12-21 19:17:01 -07:00
if [ [ " ${ reply } " = = n ] ] ; then
2016-01-23 04:05:04 -07:00
break
fi
fi
done
clean_files
2015-07-22 08:00:17 -07:00
}
2019-12-24 00:15:18 -07:00
while getopts "hlLmMVp:P:g:r:s" opt; do
2015-07-22 08:00:17 -07:00
case ${ opt } in
h)
usage
exit 0
; ;
l)
2019-07-29 22:45:59 -07:00
shift # remove opt
show_vimpatches " $@ "
2017-12-10 16:49:44 -07:00
exit 0
; ;
L)
2019-07-29 22:45:59 -07:00
shift # remove opt
2019-10-13 14:09:09 -07:00
list_missing_vimpatches 0 " $@ "
2015-07-22 08:00:17 -07:00
exit 0
; ;
2017-12-25 19:38:42 -07:00
M)
list_vimpatch_numbers
exit 0
; ;
2019-12-24 00:15:18 -07:00
m)
shift # remove opt
list_missing_previous_vimpatches_for_patch " $@ "
exit 0
; ;
2015-07-22 08:00:17 -07:00
p)
2016-12-12 18:48:55 -07:00
stage_patch " ${ OPTARG } "
2019-06-08 05:42:17 -07:00
exit
2016-12-12 18:48:55 -07:00
; ;
2017-11-06 16:29:19 -07:00
P)
stage_patch " ${ OPTARG } " TRY_APPLY
exit 0
; ;
2016-12-12 18:48:55 -07:00
g)
2017-12-16 13:38:53 -07:00
get_vimpatch " ${ OPTARG } "
2015-07-22 08:00:17 -07:00
exit 0
; ;
r)
review_pr " ${ OPTARG } "
exit 0
; ;
2016-01-23 13:45:21 -07:00
s)
submit_pr
exit 0
; ;
2017-12-10 17:58:55 -07:00
V)
2019-10-16 06:44:38 -07:00
get_vim_sources update
2017-12-10 17:58:55 -07:00
exit 0
; ;
2015-07-22 08:00:17 -07:00
*)
exit 1
; ;
esac
done
usage
# vim: et sw=2