* CI: disable Ubuntu 16.04 as it's EOL https://github.blog/changelog/2021-04-29-github-actions-ubuntu-16-04-lts-virtual-environment-will-be-removed-on-september-20-2021/ * main: lint false positive * install: lint * plugins/cmd-returned-notify: don't `export` * plugins/xterm: lint * plugins/git: lint * plugins/goenv: lint * plugins/alias-completion: lint false positives * plugins/alias-completion: fix SC2155, SC2154 Declare `locals` at the top of the function * completion: lint completions using `bash_completion` functions Match the style of the existing code * completion/knife: lint false positives * completion/knife: lint * completion/sdkman: lint * completion/composer: lint * Move `.shellcheckrc` under `themes/` * lib/theme: fix SC2155, SC2154, SC2034 * lib/colors: don't warn on unused variables We assign a large number of variables here and they may or may not be used anywhere else, so disable SC2034 for this file (only). Alsö disable SC2005 as the functions in this file were written before `printf` was invented and have to do some fancy metascripting to get escape sequences interpreted reliably. I’m not smart enough to fix this to use `printf`, so leave it for now. * themes/agnoster: lint * themes: disable SC2154 for colors Each one of these themes will need it’s own fix for SC2154, possibly upstream. Due to the way themes are, it's entirely normal to have a *lot* of false positives for SC2034. So much so, that I have to admit that it is probably just not worth linting for SC2034 despite my dislike of blanket ignore rules. * themes: disable SC2154, fix SC2155 Each one of these themes will need it’s own fix for SC2154, possibly upstream. Due to the way themes are, it's entirely normal to have a *lot* of false positives for SC2034. So much so, that I have to admit that it is probably just not worth linting for SC2034 despite my dislike of blanket ignore rules. * Delete `.shellcheckrc` * remove executable bit
317 lines
8.3 KiB
Bash
317 lines
8.3 KiB
Bash
# shellcheck shell=bash
|
|
cite about-plugin
|
|
about-plugin 'git helper functions'
|
|
|
|
# shellcheck disable=SC2016
|
|
function git_remote {
|
|
about 'adds remote $GIT_HOSTING:$1 to current repo'
|
|
group "git"
|
|
|
|
echo "Running: git remote add origin ${GIT_HOSTING:?}:$1.git"
|
|
git remote add origin "${GIT_HOSTING}:${1}".git
|
|
}
|
|
|
|
function git_first_push {
|
|
about 'push into origin refs/heads/master'
|
|
group 'git'
|
|
|
|
echo "Running: git push origin master:refs/heads/master"
|
|
git push origin master:refs/heads/master
|
|
}
|
|
|
|
function git_pub() {
|
|
about 'publishes current branch to remote origin'
|
|
group 'git'
|
|
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
|
|
echo "Publishing ${BRANCH} to remote origin"
|
|
git push -u origin "${BRANCH}"
|
|
}
|
|
|
|
function git_revert() {
|
|
about 'applies changes to HEAD that revert all changes after this commit'
|
|
group 'git'
|
|
|
|
git reset "${1:?}"
|
|
git reset --soft "HEAD@{1}"
|
|
git commit -m "Revert to ${1}"
|
|
git reset --hard
|
|
}
|
|
|
|
function git_rollback() {
|
|
about 'resets the current HEAD to this commit'
|
|
group 'git'
|
|
|
|
function is_clean() {
|
|
if [[ $(git diff --shortstat 2> /dev/null | tail -n1) != "" ]]; then
|
|
echo "Your branch is dirty, please commit your changes"
|
|
kill -INT $$
|
|
fi
|
|
}
|
|
|
|
function commit_exists() {
|
|
if git rev-list --quiet "${1:?}"; then
|
|
echo "Commit ${1} does not exist"
|
|
kill -INT $$
|
|
fi
|
|
}
|
|
|
|
function keep_changes() {
|
|
while true; do
|
|
# shellcheck disable=SC2162
|
|
read -p "Do you want to keep all changes from rolled back revisions in your working tree? [Y/N]" RESP
|
|
case "${RESP}" in
|
|
|
|
[yY])
|
|
echo "Rolling back to commit ${1} with unstaged changes"
|
|
git reset "$1"
|
|
break
|
|
;;
|
|
[nN])
|
|
echo "Rolling back to commit ${1} with a clean working tree"
|
|
git reset --hard "$1"
|
|
break
|
|
;;
|
|
*)
|
|
echo "Please enter Y or N"
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
if [ -n "$(git symbolic-ref HEAD 2> /dev/null)" ]; then
|
|
is_clean
|
|
commit_exists "$1"
|
|
|
|
while true; do
|
|
# shellcheck disable=SC2162
|
|
read -p "WARNING: This will change your history and move the current HEAD back to commit ${1}, continue? [Y/N]" RESP
|
|
case "${RESP}" in
|
|
|
|
[yY])
|
|
keep_changes "$1"
|
|
break
|
|
;;
|
|
[nN])
|
|
break
|
|
;;
|
|
*)
|
|
echo "Please enter Y or N"
|
|
;;
|
|
esac
|
|
done
|
|
else
|
|
echo "you're currently not in a git repository"
|
|
fi
|
|
}
|
|
|
|
function git_remove_missing_files() {
|
|
about "git rm's missing files"
|
|
group 'git'
|
|
|
|
git ls-files -d -z | xargs -0 git update-index --remove
|
|
}
|
|
|
|
# Adds files to git's exclude file (same as .gitignore)
|
|
function local-ignore() {
|
|
about 'adds file or path to git exclude file'
|
|
param '1: file or path fragment to ignore'
|
|
group 'git'
|
|
echo "$1" >> .git/info/exclude
|
|
}
|
|
|
|
# get a quick overview for your git repo
|
|
function git_info() {
|
|
about 'overview for your git repo'
|
|
group 'git'
|
|
|
|
if [ -n "$(git symbolic-ref HEAD 2> /dev/null)" ]; then
|
|
# print informations
|
|
echo "git repo overview"
|
|
echo "-----------------"
|
|
echo
|
|
|
|
# print all remotes and thier details
|
|
for remote in $(git remote show); do
|
|
echo "${remote}":
|
|
git remote show "${remote}"
|
|
echo
|
|
done
|
|
|
|
# print status of working repo
|
|
echo "status:"
|
|
if [ -n "$(git status -s 2> /dev/null)" ]; then
|
|
git status -s
|
|
else
|
|
echo "working directory is clean"
|
|
fi
|
|
|
|
# print at least 5 last log entries
|
|
echo
|
|
echo "log:"
|
|
git log -5 --oneline
|
|
echo
|
|
|
|
else
|
|
echo "you're currently not in a git repository"
|
|
|
|
fi
|
|
}
|
|
|
|
function git_stats {
|
|
about 'display stats per author'
|
|
group 'git'
|
|
|
|
# awesome work from https://github.com/esc/git-stats
|
|
# including some modifications
|
|
|
|
if [ -n "$(git symbolic-ref HEAD 2> /dev/null)" ]; then
|
|
echo "Number of commits per author:"
|
|
git --no-pager shortlog -sn --all
|
|
AUTHORS=$(git shortlog -sn --all | cut -f2 | cut -f1 -d' ')
|
|
LOGOPTS=""
|
|
if [ "$1" == '-w' ]; then
|
|
LOGOPTS="${LOGOPTS} -w"
|
|
shift
|
|
fi
|
|
if [ "$1" == '-M' ]; then
|
|
LOGOPTS="${LOGOPTS} -M"
|
|
shift
|
|
fi
|
|
if [ "$1" == '-C' ]; then
|
|
LOGOPTS="${LOGOPTS} -C --find-copies-harder"
|
|
shift
|
|
fi
|
|
for a in ${AUTHORS}; do
|
|
echo '-------------------'
|
|
echo "Statistics for: ${a}"
|
|
echo -n "Number of files changed: "
|
|
# shellcheck disable=SC2086
|
|
git log ${LOGOPTS} --all --numstat --format="%n" --author="${a}" | cut -f3 | sort -iu | wc -l
|
|
echo -n "Number of lines added: "
|
|
# shellcheck disable=SC2086
|
|
git log ${LOGOPTS} --all --numstat --format="%n" --author="${a}" | cut -f1 | awk '{s+=$1} END {print s}'
|
|
echo -n "Number of lines deleted: "
|
|
# shellcheck disable=SC2086
|
|
git log ${LOGOPTS} --all --numstat --format="%n" --author="${a}" | cut -f2 | awk '{s+=$1} END {print s}'
|
|
echo -n "Number of merges: "
|
|
# shellcheck disable=SC2086
|
|
git log ${LOGOPTS} --all --merges --author="${a}" | grep -c '^commit'
|
|
done
|
|
else
|
|
echo "you're currently not in a git repository"
|
|
fi
|
|
}
|
|
|
|
function gittowork() {
|
|
about 'Places the latest .gitignore file for a given project type in the current directory, or concatenates onto an existing .gitignore'
|
|
group 'git'
|
|
param '1: the language/type of the project, used for determining the contents of the .gitignore file'
|
|
example '$ gittowork java'
|
|
|
|
result=$(curl -L "https://www.gitignore.io/api/$1" 2> /dev/null)
|
|
|
|
if [[ "${result}" =~ ERROR ]]; then
|
|
echo "Query '$1' has no match. See a list of possible queries with 'gittowork list'"
|
|
elif [[ $1 == list ]]; then
|
|
echo "${result}"
|
|
else
|
|
if [[ -f .gitignore ]]; then
|
|
result=$(grep -v "# Created by http://www.gitignore.io" <<< "${result}")
|
|
echo ".gitignore already exists, appending..."
|
|
fi
|
|
echo "${result}" >> .gitignore
|
|
fi
|
|
}
|
|
|
|
function gitignore-reload() {
|
|
about 'Empties the git cache, and readds all files not blacklisted by .gitignore'
|
|
group 'git'
|
|
example '$ gitignore-reload'
|
|
|
|
# The .gitignore file should not be reloaded if there are uncommited changes.
|
|
# Firstly, require a clean work tree. The function require_clean_work_tree()
|
|
# was stolen with love from https://www.spinics.net/lists/git/msg142043.html
|
|
|
|
# Begin require_clean_work_tree()
|
|
|
|
# Update the index
|
|
git update-index -q --ignore-submodules --refresh
|
|
err=0
|
|
|
|
# Disallow unstaged changes in the working tree
|
|
if ! git diff-files --quiet --ignore-submodules --; then
|
|
echo >&2 "ERROR: Cannot reload .gitignore: Your index contains unstaged changes."
|
|
git diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2
|
|
err=1
|
|
fi
|
|
|
|
# Disallow uncommited changes in the index
|
|
if ! git diff-index --cached --quiet HEAD --ignore-submodules; then
|
|
echo >&2 "ERROR: Cannot reload .gitignore: Your index contains uncommited changes."
|
|
git diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2
|
|
err=1
|
|
fi
|
|
|
|
# Prompt user to commit or stash changes and exit
|
|
if [[ "${err}" == 1 ]]; then
|
|
echo >&2 "Please commit or stash them."
|
|
fi
|
|
|
|
# End require_clean_work_tree()
|
|
|
|
# If we're here, then there are no uncommited or unstaged changes dangling around.
|
|
# Proceed to reload .gitignore
|
|
if [[ "${err}" == 0 ]]; then
|
|
# Remove all cached files
|
|
git rm -r --cached .
|
|
|
|
# Re-add everything. The changed .gitignore will be picked up here and will exclude the files
|
|
# now blacklisted by .gitignore
|
|
echo >&2 "Running git add ."
|
|
git add .
|
|
echo >&2 "Files readded. Commit your new changes now."
|
|
fi
|
|
}
|
|
|
|
function git-changelog() {
|
|
# ---------------------------------------------------------------
|
|
# ORIGINAL ANSWER: https://stackoverflow.com/a/2979587/10362396 |
|
|
# ---------------------------------------------------------------
|
|
about 'Creates the git changelog from one point to another by date'
|
|
group 'git'
|
|
example '$ git-changelog origin/master...origin/release [md|txt]'
|
|
|
|
if [[ "$1" != *"..."* ]]; then
|
|
echo "Please include the valid 'diff' to make changelog"
|
|
return 1
|
|
fi
|
|
|
|
# shellcheck disable=SC2155
|
|
local NEXT=$(date +%F)
|
|
|
|
if [[ "$2" == "md" ]]; then
|
|
echo "# CHANGELOG $1"
|
|
|
|
# shellcheck disable=SC2162
|
|
git log "$1" --no-merges --format="%cd" --date=short | sort -u -r | while read DATE; do
|
|
echo
|
|
echo "### ${DATE}"
|
|
git log --no-merges --format=" * (%h) %s by [%an](mailto:%ae)" --since="${DATE} 00:00:00" --until="${DATE} 24:00:00"
|
|
NEXT=${DATE}
|
|
done
|
|
else
|
|
echo "CHANGELOG $1"
|
|
echo ----------------------
|
|
|
|
# shellcheck disable=SC2162
|
|
git log "$1" --no-merges --format="%cd" --date=short | sort -u -r | while read DATE; do
|
|
echo
|
|
echo "[${DATE}]"
|
|
git log --no-merges --format=" * (%h) %s by %an <%ae>" --since="${DATE} 00:00:00" --until="${DATE} 24:00:00"
|
|
# shellcheck disable=SC2034
|
|
NEXT=${DATE}
|
|
done
|
|
fi
|
|
}
|