You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
778 lines
22 KiB
Bash
778 lines
22 KiB
Bash
#!/usr/bin/env bash
|
|
# vim:ts=4:sts=4:sw=4:et
|
|
#
|
|
# Author: Hari Sekhon
|
|
# Date: circa 2006 (forked from .bashrc)
|
|
#
|
|
# https://github.com/harisekhon/bash-tools
|
|
#
|
|
# License: see accompanying Hari Sekhon LICENSE file
|
|
#
|
|
# If you're using my code you're welcome to connect with me on LinkedIn and optionally send me feedback to help steer this or other code I publish
|
|
#
|
|
# https://www.linkedin.com/in/harisekhon
|
|
#
|
|
|
|
# ============================================================================ #
|
|
# R e v i s i o n C o n t r o l - G i t
|
|
# ============================================================================ #
|
|
|
|
# Primary revision control system
|
|
#
|
|
# if svn.sh and hg.sh functions are enabled, detects and calls svn and mercurial commands if inside those repos so some of the same commands work dynamically
|
|
|
|
if [ -f ~/.github_token ]; then
|
|
GITHUB_TOKEN="$(cat ~/.github_token)"
|
|
export GITHUB_TOKEN
|
|
fi
|
|
|
|
if [ -f ~/.gitlab_token ]; then
|
|
GITLAB_API_PRIVATE_TOKEN="$(cat ~/.gitlab_token)"
|
|
export GITLAB_API_PRIVATE_TOKEN
|
|
fi
|
|
if [ -z "${GITLAB_API_ENDPOINT:-}" ]; then
|
|
export GITLAB_API_ENDPOINT="https://gitlab.com/api/v3"
|
|
fi
|
|
|
|
# set location where you check out all the github repos
|
|
export github=~/github
|
|
|
|
export GIT_PAGER="less ${LESS:-}"
|
|
# shellcheck disable=SC2230
|
|
#if [ -z "${GIT_PAGER:-}" ] && \
|
|
if type -P diff-so-fancy &>/dev/null; then
|
|
# pre-loading a pattern to 'n' / 'N' / '?' / '/' search through will force you in to pager and disregard -F / --quit-if-one-screen
|
|
#export GIT_PAGER="diff-so-fancy --color=yes | less -RFX --tabs=4 --pattern '^(Date|added|deleted|modified): '"
|
|
export GIT_PAGER="diff-so-fancy --color=yes | $GIT_PAGER"
|
|
fi
|
|
|
|
alias gitconfig="\$EDITOR ~/.gitconfig"
|
|
alias gitignore="\$EDITOR ~/.gitignore_global"
|
|
alias gitrc=gitconfig
|
|
# false positive, not calling this from xargs
|
|
# shellcheck disable=SC2032
|
|
alias add=gitadd
|
|
alias gadd='git add'
|
|
alias import=gitimport
|
|
alias co=checkout
|
|
alias commit="git commit"
|
|
alias clone="git clone"
|
|
alias gitci=commit
|
|
alias ci=commit
|
|
alias gitco=checkout
|
|
alias up=pull
|
|
alias u=up
|
|
alias pu=push
|
|
alias gitp="git push"
|
|
alias gdiff="git diff"
|
|
# bypasses diff-so-fancy, could also just pipe through | cat to disable pager and color effects
|
|
alias gdiff2="git --no-pager diff"
|
|
alias gdiffc="git diff --cached"
|
|
alias gdiffm="gdiff origin/master.."
|
|
alias gd=gdiff
|
|
alias gdc=gdiffc
|
|
alias gdo=gdiffm
|
|
alias branch="githg branch"
|
|
alias br=branch
|
|
alias fetch='git fetch'
|
|
alias stash="git stash"
|
|
alias tag="githg tag"
|
|
alias um=updatemodules
|
|
#type browse &>/dev/null || alias browse=gbrowse
|
|
alias gbrowse=gitbrowse
|
|
alias gh=gitbrowse
|
|
alias github_actions='gitbrowse actions github'
|
|
alias github_workflows='github_actions'
|
|
alias gha='github_actions'
|
|
alias ghw='github_workflows'
|
|
alias wf='cd .github/workflows/'
|
|
alias ggrep="git grep"
|
|
alias remotes='git remote -v'
|
|
alias remote='remotes'
|
|
# much quicker to just 'cd $github; f <pattern>'
|
|
#githubls(){
|
|
# # GitHub is svn compatible, use this to list files remotely
|
|
# svn ls "https://github.com/$1.git/branches/master/"
|
|
#}
|
|
#githubgrep(){
|
|
# for repo in $(sed 's/#.*//;s/:.*//;/^[[:space:]]*$/d' "$srcdir/setup/repolist.txt"); do
|
|
# githubls "HariSekhon/$repo"
|
|
# done |
|
|
# grep "$@"
|
|
#}
|
|
|
|
# git fetch -p or git remote prune origin
|
|
alias prune="co master; git pull; git remote prune origin; git branch --merged | grep -v -e '^\\*' -e 'master' | xargs git branch -d"
|
|
|
|
# don't use this unless you are a git pro and understand unwinding history and merge conflicts
|
|
alias GRH="git reset HEAD^"
|
|
|
|
alias master="switchbranch master"
|
|
alias prod="switchbranch prod"
|
|
alias staging="switchbranch staging"
|
|
alias stage=staging
|
|
alias dev="switchbranch dev"
|
|
|
|
# equivalent of hg root
|
|
git_root(){
|
|
git rev-parse --show-toplevel
|
|
}
|
|
|
|
gitgc(){
|
|
if ! [ -d .git ]; then
|
|
echo "not at top of a git repo, not .git/ directory found"
|
|
return 1
|
|
fi
|
|
du -sh .git
|
|
git gc --aggressive
|
|
du -sh .git
|
|
}
|
|
|
|
gitbrowse(){
|
|
local url
|
|
local filter="${2:-.*}"
|
|
url="$(git remote -v | grep "$filter" | awk '/git@|https:/{print $2}' | sed 's,://.*@,://,; s|git@github.com:|https://github.com/| ; s/\.git$//' | head -n1)"
|
|
if [ $# -gt 0 ] &&
|
|
[ -z "$url" ]; then
|
|
echo "git remote url not found"
|
|
return 1
|
|
fi
|
|
browser "$url/$1"
|
|
}
|
|
|
|
install_git_completion(){
|
|
if ! [ -f ~/.git-completion.bash ]; then
|
|
wget -O ~/.git-completion.bash https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash
|
|
fi
|
|
}
|
|
|
|
# shellcheck disable=SC1090
|
|
[ -f ~/.git-completion.bash ] && . ~/.git-completion.bash
|
|
|
|
# usage: gi python,perl,go
|
|
# gi list
|
|
gitignore_api(){
|
|
local url
|
|
local langs
|
|
local options=()
|
|
local args=()
|
|
# noop - set to use 'tr' to separate items to newlines when given the 'list' arg
|
|
local commas_to_newlines="cat"
|
|
for arg; do
|
|
if [ "$arg" = -- ]; then
|
|
options+=("$arg")
|
|
else
|
|
args+=("$arg")
|
|
fi
|
|
done
|
|
# take args 'python perl', store as 'python,perl' for the API call
|
|
langs="$(IFS=, ; echo "${args[*]}")"
|
|
url="https://www.gitignore.io/api/$langs"
|
|
if [ "$langs" = "list" ]; then
|
|
commas_to_newlines="tr ',' '\\n'"
|
|
fi
|
|
{
|
|
if hash curl 2>/dev/null; then
|
|
curl -sSL "${options[*]}" "$url"
|
|
elif hash wget 2>/dev/null; then
|
|
wget -O - "${options[*]}" "$url"
|
|
fi
|
|
} | eval "$commas_to_newlines"
|
|
echo
|
|
}
|
|
alias gi=gitignore_api
|
|
|
|
git_repo(){
|
|
git remote -v | awk '{print $2}' | head -n1 | sed 's/[[:alnum:]]*@//; s,.*github.com[/:]*,,'
|
|
}
|
|
|
|
isGit(){
|
|
local target=${1:-.}
|
|
# There aren't local .hg dirs everywhere only at top level so this is difficult in bash
|
|
if [ -d "$target/.git" ]; then
|
|
return 0
|
|
elif [ -f "$target" ] &&
|
|
[ -d "${target%/*}/.git" ]; then
|
|
#-o "$target/../.git" -o "${target%/*}/../.git" ]; then
|
|
return 0
|
|
else
|
|
# This is because git command doesn't return correctly when running from outside git root, complains there is not .git
|
|
pushd "$(dirname "$target")" >/dev/null || return 1
|
|
# subdirs which are not handled by Git fail isGit
|
|
# returns false for a newly added not committed dir
|
|
#if git log -1 "$target" 2>/dev/null | grep -q '.*'; then
|
|
if [ -n "$(git log -1 "$(basename "$target")" 2>/dev/null)" ]; then
|
|
# shellcheck disable=SC2164
|
|
popd &>/dev/null
|
|
return 0
|
|
fi
|
|
# shellcheck disable=SC2164
|
|
popd &>/dev/null
|
|
return 2
|
|
fi
|
|
}
|
|
|
|
|
|
st(){
|
|
# shellcheck disable=SC2086
|
|
{
|
|
local target="${1:-.}"
|
|
shift
|
|
if ! [ -e "$target" ]; then
|
|
echo "$target does not exist"
|
|
return 1
|
|
fi
|
|
local target_basename
|
|
local target_dirname
|
|
target_basename="$(basename "$target")"
|
|
target_dirname="$(dirname "$target")"
|
|
#if [ -f "Vagrantfile" ]; then
|
|
# echo "> vagrant status"
|
|
# vagrant status
|
|
# shellcheck disable=SC2166
|
|
if [ "$target_basename" = "github" ] ||
|
|
[ "$target" = "." -a "$(pwd)" = ~/github ]; then
|
|
hr
|
|
for x in "$target"/*; do
|
|
[ -d "$x" ] || continue
|
|
pushd "$x" >/dev/null || { echo "failed to pushd to '$x'"; return 1; }
|
|
if git remote -v | grep -qi harisekhon; then
|
|
echo "> GitHub: git status $x $*"
|
|
git status . "$@"
|
|
echo
|
|
hr
|
|
echo
|
|
fi
|
|
# shellcheck disable=SC2164
|
|
popd &>/dev/null
|
|
done
|
|
elif isGit "$target"; then
|
|
if [ -d "$target" ]; then
|
|
pushd "$target" >/dev/null || { echo "Error: failed to pushd to $target"; return 1; }
|
|
echo "> git stash list" >&2
|
|
git stash list && echo
|
|
echo "> git status $target $*" >&2
|
|
#git -c color.status=always status -sb . "$@"
|
|
git -c color.status=always status . "$@"
|
|
else
|
|
pushd "$target_dirname" >/dev/null || { echo "Error: failed to pushed to '$target_dirname'"; return 1; }
|
|
echo "> git status $target $*" >&2
|
|
git -c color.status=always status "$target_basename" "$@"
|
|
fi
|
|
#git status "$target" "${*:2}"
|
|
# shellcheck disable=SC2164
|
|
popd &>/dev/null
|
|
elif type isHg &>/dev/null && isHg "$target"; then
|
|
echo "> hg status $target $*" >&2
|
|
hg status "$target" "$@" | grep -v "^?"
|
|
# to see relative paths instead of the default absolute paths
|
|
#hg status "$(hg root)"
|
|
elif type isSvn &>/dev/null && isSvn "$target"; then
|
|
echo "> svn st $*" >&2
|
|
svn st --ignore-externals "$target" "$@" | grep -v -e "^?" -e "^x";
|
|
else
|
|
echo "not a revision controlled resource as far as bashrc can tell"
|
|
fi
|
|
} |
|
|
# more calls less on Mac, and gets stuck in interactive mode ignoring the less alias switches
|
|
#more -R -n "$((LINES - 3))"
|
|
#less -RFX
|
|
eval ${GIT_PAGER:-cat}
|
|
}
|
|
|
|
stq(){
|
|
st "$@" | grep --color=no -e "=======" -e branch -e GitHub | eval "${GIT_PAGER:-cat}"
|
|
}
|
|
|
|
# disabling this as I don't use Mercurial or Svn any more,
|
|
# replacing with simpler function below that will pass through more things like --rebase
|
|
#pull(){
|
|
# local target="${1:-.}"
|
|
# if ! [ -e "$target" ]; then
|
|
# echo "$target does not exist"
|
|
# return 1
|
|
# fi
|
|
# local target_basename
|
|
# target_basename="$(basename "$target")"
|
|
# # shellcheck disable=SC2166
|
|
# if [ "$target_basename" = "github" ] || [ "$target" = "." -a "$(pwd)" = "$github" ]; then
|
|
# for x in "$target"/*; do
|
|
# [ -d "$x" ] || continue
|
|
# # get last character of string
|
|
# [ "${x: -1}" = 2 ] && continue
|
|
# pushd "$x" >/dev/null || { echo "failed to pushd to '$x'"; return 1; }
|
|
# if git remote -v | grep -qi harisekhon; then
|
|
# echo "> GitHub: git pull $x ${*:2}"
|
|
# git pull "${@:2}"
|
|
# echo
|
|
# echo "> GitHub: git submodule update --init --recursive"
|
|
# git submodule update --init --recursive
|
|
# echo
|
|
# fi
|
|
# # shellcheck disable=SC2164
|
|
# popd &>/dev/null
|
|
# done
|
|
# return
|
|
# elif isGit "$target"; then
|
|
# pushd "$target" >/dev/null &&
|
|
# echo "> git pull -v ${*:2}" >&2
|
|
# git pull -v "${@:2}"
|
|
# echo "> git submodule update --init --recursive"
|
|
# git submodule update --init --recursive
|
|
# #local orig_branch=$(git branch | awk '/^\*/ {print $2}')
|
|
# #for branch in $(git branch | cut -c 3- ); do
|
|
# # git checkout -q "$branch" &&
|
|
# # echo -n "$branch => " &&
|
|
# # git pull -v
|
|
# # echo
|
|
# # echo
|
|
# #done
|
|
# #git checkout -q "$orig_branch"
|
|
# # shellcheck disable=SC2164
|
|
# popd &>/dev/null
|
|
# elif type isHg &>/dev/null && isHg "$target"; then
|
|
# pushd "$target" >/dev/null &&
|
|
# echo "> hg pull && hg up" >&2 &&
|
|
# hg pull && hg up
|
|
# # shellcheck disable=SC2164
|
|
# popd &>/dev/null
|
|
# elif type isSvn &>/dev/null && isSvn "$target"; then
|
|
# echo "> svn up $target" >&2
|
|
# svn up "$target"
|
|
# else
|
|
# echo "not a revision controlled resource as far as bashrc can tell"
|
|
# return 1
|
|
# fi
|
|
#}
|
|
|
|
# simpler replacement function to above
|
|
pull(){
|
|
# shellcheck disable=SC2166
|
|
if [ "${PWD##*/}" = github ]; then
|
|
for x in *; do
|
|
[ -d "$x" ] || continue
|
|
# get last character of string - don't pull blah2, as I use them as clean checkouts
|
|
[ "${x: -1}" = 2 ] && continue
|
|
pushd "$x" >/dev/null || { echo "failed to pushd to '$x'"; return 1; }
|
|
if git remote -v | grep -qi "${GITHUB_USER:-${GIT_USER:-${USER:-}}}"; then
|
|
echo "> GitHub $x: git pull --all --no-edit $*"
|
|
git pull --all --no-edit "$@"
|
|
echo
|
|
echo "> GitHub $x: git submodule update --init --recursive"
|
|
git submodule update --init --recursive
|
|
echo
|
|
fi
|
|
# shellcheck disable=SC2164
|
|
popd &>/dev/null
|
|
done
|
|
return
|
|
else
|
|
echo "> git pull --all --no-edit $*"
|
|
git pull --all --no-edit "$@"
|
|
echo "> git submodule update --init --recursive"
|
|
git submodule update --init --recursive
|
|
fi
|
|
}
|
|
|
|
checkout(){
|
|
if isGit "."; then
|
|
git checkout "$@";
|
|
else
|
|
echo "not a Git checkout, cannot switch to branch $*"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
gitadd() {
|
|
local gitcimsg=""
|
|
for x in "$@"; do
|
|
if git status -s "$x" | grep -q '^[?A]'; then
|
|
gitcimsg+="$x, "
|
|
fi
|
|
done
|
|
[ -z "$gitcimsg" ] && return 1
|
|
gitcimsg="${gitcimsg%, }"
|
|
gitcimsg="added $gitcimsg"
|
|
git add "$@" &&
|
|
git commit -m "$gitcimsg" "$@"
|
|
}
|
|
|
|
gitimport() {
|
|
local gitcimsg=""
|
|
for x in "$@"; do
|
|
if git status -s "$x" | grep -q '^[?A]'; then
|
|
gitcimsg+="$x, "
|
|
fi
|
|
done
|
|
[ -z "$gitcimsg" ] && return 1
|
|
gitcimsg="${gitcimsg%, }"
|
|
gitcimsg="imported $gitcimsg"
|
|
git add "$@" &&
|
|
git commit -m "$gitcimsg" "$@"
|
|
}
|
|
|
|
# shellcheck disable=SC2086
|
|
gitu(){
|
|
if [ -z "$1" ]; then
|
|
echo "usage: gitu <file>"
|
|
return 3
|
|
fi
|
|
local targets
|
|
if [ -n "$(git diff "$@")" ]; then
|
|
targets="$*"
|
|
else
|
|
# follow symlinks to the actual files because diffing symlinks returns no changes
|
|
targets="$(resolve_symlinks "$@")"
|
|
fi
|
|
local basedir
|
|
# go to the highest directory level to git diff inside the git repo boundary, otherwise git diff will return nothing
|
|
basedir="$(basedir $targets)" &&
|
|
local trap_codes="INT ERR"
|
|
# expand now
|
|
# shellcheck disable=SC2064
|
|
trap "popd &>/dev/null; trap - $trap_codes; return 1" $trap_codes
|
|
pushd "$basedir" >/dev/null || return 1
|
|
targets="$(strip_basedirs $basedir $targets)"
|
|
# shellcheck disable=SC2086
|
|
if [ -z "$(git diff $targets)" ]; then
|
|
popd &>/dev/null || :
|
|
return 0
|
|
fi
|
|
# shellcheck disable=SC2086
|
|
git diff $targets &&
|
|
read -r &&
|
|
git add $targets &&
|
|
echo "committing $targets" &&
|
|
git commit -m "updated $targets" $targets
|
|
popd &>/dev/null || :
|
|
trap - $trap_codes
|
|
}
|
|
|
|
#githgu(){
|
|
# target="${1:-.}"
|
|
# #count=0
|
|
# while [ -L "$target" ]; do
|
|
# #target="$(readlink "$target")"
|
|
# #let count+=1
|
|
# #if [ $count -gt 10 ]; then
|
|
# # echo "looping over links more than 10 times in hggitu! "
|
|
# # exit 2
|
|
# #fi
|
|
# echo "$target is a symlink! "
|
|
# return 1
|
|
# done
|
|
# if ! [ -e "$target" ]; then
|
|
# echo "$target does not exist"
|
|
# return 1
|
|
# fi
|
|
# if isGit "$target"; then
|
|
# echo "> git" >&2
|
|
# #if [ -d "$target" ]; then
|
|
# # pushd "$target" >/dev/null
|
|
# #else
|
|
# # pushd "$(dirname "$target")" >/dev/null
|
|
# #fi
|
|
# #"$srcdir2/gitu" "${target##*/}" &&
|
|
# gitu "$target"
|
|
# #popd &>/dev/null
|
|
# elif type isHg &>/dev/null && isHg "$target"; then
|
|
# echo "> hg" >&2
|
|
# #if [ -d "$target" ]; then
|
|
# # pushd "$target" >/dev/null
|
|
# #else
|
|
# # pushd "$(dirname "$target")" >/dev/null
|
|
# #fi
|
|
# #"$srcdir2/hgu" "${target##*/}" &&
|
|
# hgu "$target"
|
|
# #popd &>/dev/null
|
|
# # Not supporting SVN any more
|
|
# #elif type isSvn &>/dev/null && isSvn "$target"; then
|
|
# # echo "> svn" >&2
|
|
# # svnu "$target"
|
|
# else
|
|
# echo "not a revision controlled resource as far as bashrc can tell"
|
|
# return 1
|
|
# fi
|
|
#}
|
|
|
|
push(){
|
|
pull "$@" || return 1
|
|
if isGit .; then
|
|
echo "> git push -v $*"
|
|
#for remote in $(git remote); do
|
|
# git push -v $remote $@
|
|
#done
|
|
git push -v "$@"
|
|
elif type isHg &>/dev/null && isHg .; then
|
|
echo "> hg push $*"
|
|
hg push "$@"
|
|
else
|
|
echo "not in a Git or Mercurial controlled directory"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
switchbranch(){
|
|
if isGit "."; then
|
|
git checkout "$1";
|
|
elif type isHg &>/dev/null && isHg "."; then
|
|
hg update "$1"
|
|
else
|
|
echo "not a Git / Mercurial checkout, cannot switch to branch $1"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
gitrm(){
|
|
git rm "$@" &&
|
|
git commit -m "removed $*" "$@"
|
|
}
|
|
|
|
gitrename(){
|
|
git mv "$1" "$2" &&
|
|
git commit -m "renamed $1 to $2" "$1" "$2"
|
|
}
|
|
|
|
gitmv(){
|
|
git mv "$1" "$2" &&
|
|
git commit -m "moved $1 to $2" "$1" "$2"
|
|
}
|
|
|
|
gitd(){
|
|
git diff "${@:-.}"
|
|
}
|
|
|
|
gitadded(){
|
|
git log --name-status "$@" |
|
|
grep -e '^A[^u]' -e '^Date' |
|
|
grep -B 1 '^A' |
|
|
less
|
|
}
|
|
|
|
# doesn't need pipe | less, git drops you in to less anyway
|
|
gitl(){
|
|
git log --all --name-status --graph --decorate "$@"
|
|
}
|
|
|
|
gitlp(){
|
|
git log -p "$@"
|
|
}
|
|
|
|
gitl2(){
|
|
git log --pretty=format:"%n%an => %ar%n%s" --name-status "$@"
|
|
}
|
|
|
|
githg(){
|
|
if isGit .; then
|
|
git "$@"
|
|
elif type isHg &>/dev/null && isHg .; then
|
|
hg "$@"
|
|
else
|
|
echo "not a Git/Mercurial checkout"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
retag(){
|
|
local tag1="$1"
|
|
local checksum="$2"
|
|
local additional_tags="${*:2}"
|
|
for tag in $tag1 $additional_tags; do
|
|
git tag -d "$tag" || :
|
|
echo "Creating git tag '$tag'"
|
|
# quoting checksum causes failure with unrecognized checksum ''
|
|
git tag "$tag" "$checksum"
|
|
git tag |
|
|
grep -qF "$tag" ||
|
|
echo "FAILED"
|
|
done
|
|
}
|
|
|
|
gitfind(){
|
|
local refids
|
|
refids="$(git log --all --oneline | grep "$@" | awk '{print $1}')"
|
|
printf 'Branches:\n\n'
|
|
for refid in $refids; do
|
|
git branch --contains "$refid"
|
|
done | sort -u
|
|
printf '\nTags:\n\n'
|
|
for refid in $refids; do
|
|
git tag --contains "$refid"
|
|
done | sort -u
|
|
}
|
|
|
|
updatemodules(){
|
|
if isGit .; then
|
|
git pull
|
|
#git submodule update --init --remote
|
|
for submodule in $(git submodule | awk '{print $2}'); do
|
|
if [ -d "$submodule" ] && ! [ -L "$submodule" ]; then
|
|
pushd "$submodule" || continue
|
|
git stash
|
|
git checkout master
|
|
git pull
|
|
git submodule update
|
|
# shellcheck disable=SC2164
|
|
popd
|
|
fi
|
|
done
|
|
echo
|
|
for submodule in $(git submodule | awk '{print $2}'); do
|
|
if [ -d "$submodule" ] && ! [ -L "$submodule" ] && ! git status "$submodule" | grep -q nothing; then
|
|
git commit -m "updated $submodule" "$submodule" || break
|
|
fi
|
|
done &&
|
|
make updatem ||
|
|
echo FAILED
|
|
echo
|
|
for submodule in $(git submodule | awk '{print $2}'); do
|
|
if [ -d "$submodule" ] && ! [ -L "$submodule" ]; then
|
|
pushd "$submodule" || continue
|
|
git stash pop
|
|
# shellcheck disable=SC2164
|
|
popd
|
|
fi
|
|
done
|
|
else
|
|
echo "Not a Git repository! "
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
upl(){
|
|
local repos="pylib pytools lib tools bash-tools nagios-plugins npk"
|
|
# pull all repos first so can handle merge requests if needed
|
|
for repo in $repos; do
|
|
echo
|
|
echo "* Pulling latest repo changes: $repo"
|
|
echo
|
|
pushd "$github/$repo" &&
|
|
git pull &&
|
|
popd &&
|
|
hr || return 1
|
|
done
|
|
echo
|
|
echo "UNATTEND FROM HERE"
|
|
echo
|
|
for repo in $repos; do
|
|
echo
|
|
echo "* Performing latest submodule updates: $repo"
|
|
echo
|
|
pushd "$github/$repo" &&
|
|
! updatemodules 2>&1 | tee /dev/stderr | grep -e ERROR -e FAIL &&
|
|
git push &&
|
|
popd &&
|
|
hr || return 1
|
|
done
|
|
}
|
|
|
|
#stagemerge(){
|
|
# if isGit "."; then
|
|
# git checkout prod && git pull &&
|
|
# git checkout staging && git pull &&
|
|
# git merge prod
|
|
# git checkout prod
|
|
# else
|
|
# echo "Not a Git working copy";
|
|
# fi
|
|
#}
|
|
|
|
gitdiff(){
|
|
local filename="${1:-}"
|
|
[ -n "$filename" ] || { echo "usage: gitdiff filename"; return 1; }
|
|
git diff "$filename" > "/tmp/gitdiff.tmp"
|
|
diffnet.pl "/tmp/hgdiff.tmp"
|
|
}
|
|
|
|
git_author_names(){
|
|
git log --all --pretty=format:"%an" | sort | uniq -c | sort -k1nr | less
|
|
}
|
|
|
|
git_author_emails(){
|
|
git log --all --pretty=format:"%ae" | sort | uniq -c | sort -k1nr | less
|
|
}
|
|
|
|
git_author_names_emails(){
|
|
git log --all --pretty=format:"%an %ae" | sort | uniq -c | sort -k1nr | less
|
|
}
|
|
|
|
git_authors(){
|
|
git_author_emails
|
|
}
|
|
|
|
git_commit_count(){
|
|
# interestingly, even on 10,000 commit repos, there are no duplicate short hashes shown from:
|
|
# git log --all --pretty=format:"%h" | sort | uniq -d
|
|
git log --all --pretty=format:"%h" | wc -l
|
|
}
|
|
|
|
git_revert_typechange(){
|
|
# want splitting to separate filenames
|
|
# shellcheck disable=SC2046
|
|
co $(git status --porcelain -s "${1:-.}" | awk '/^.T/{print $2}')
|
|
}
|
|
|
|
git_rm_untracked(){
|
|
if [ $# -lt 1 ]; then
|
|
echo "usage: rm_untracked <target_dir_or_files_or_glob>"
|
|
return 1
|
|
fi
|
|
# iterate on explicit targets only
|
|
# intentionally not including current directory to avoid accidentally wiping out untracked files - you must specify "rm_untracked ." if you really intend this
|
|
for x in "${@:-}"; do
|
|
# want splitting to separate filenames
|
|
# shellcheck disable=SC2046
|
|
rm -v $(git status --porcelain -s "$x" | awk '/^\?\?/{print $2}')
|
|
done
|
|
}
|
|
|
|
# example of usage of this in the function below - make sure to put '$repo' or "\$repo" somewhere in the argument body to make use of the iteration variable
|
|
foreachrepo(){
|
|
local repolist="${REPOLIST:-$bash_tools/setup/repolist.txt}"
|
|
while read -r repo; do
|
|
eval "$@"
|
|
done < <(sed 's/#.*$//; s/.*://; /^[[:space:]]*$/d' "$repolist")
|
|
}
|
|
|
|
github_authors(){
|
|
# deferring expansion into loop
|
|
# shellcheck disable=SC2016
|
|
foreachrepo 'echo "repo: $repo"; pushd "$github/$repo" >/dev/null || return 1; git_authors; popd >/dev/null || return 1; echo' | ${less:-less}
|
|
}
|
|
|
|
merge_conflicting_files(){
|
|
# merge conflicts:
|
|
#
|
|
# UU = both updated
|
|
# AA = both added
|
|
#
|
|
git status --porcelain | awk '/^UU|^AA/{$1=""; print}'
|
|
}
|
|
|
|
merge_deleted_files(){
|
|
git status --porcelain | awk '/^DU/{$1=""; print}'
|
|
}
|
|
|
|
# useful for Dockerfiles merging lots of branches
|
|
#
|
|
# while ! make mergemasterpull; do fixmerge "merged master"; done
|
|
#
|
|
fixmerge(){
|
|
local msg="${*:-merged}"
|
|
local merge_conflicted_files
|
|
local merge_deleted_files
|
|
merge_deleted_files="$(merge_deleted_files)"
|
|
if [ -n "$merge_deleted_files" ]; then
|
|
# false positive, not passing add function/alias add to git
|
|
# shellcheck disable=SC2033
|
|
xargs git add <<< "$merge_deleted_files"
|
|
fi
|
|
merge_conflicted_files="$(merge_conflicting_files)"
|
|
if [ -n "$merge_conflicted_files" ]; then
|
|
# shellcheck disable=SC2086
|
|
"$EDITOR" $merge_conflicted_files &&
|
|
git add $merge_conflicted_files
|
|
fi
|
|
git ci -m "$msg"
|
|
}
|