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.

417 lines
12 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
# set location where you check out all the github repos
export github=~/github
alias gitconfig="\$EDITOR \~/.gitconfig"
alias gitignore="\$EDITOR \~/.gitignore_global"
alias gitrc=gitconfig
alias co=checkout
alias ci=commit
alias gitco=checkout
alias gitci=commit
alias up=pull
alias u=up
alias gdiff="git diff"
alias branch="githg branch"
alias br=branch
alias tag="githg tag"
alias um=updatemodules
# git fetch -p or git remote prune origin
alias prune="co master; git pull; git remote prune origin; git branch | grep -v -e '^\*' -e 'master' | xargs git branch -d"
alias master="switchbranch master"
alias prod="switchbranch prod"
alias staging="switchbranch staging"
alias stage=staging
alias dev="switchbranch dev"
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
isGit(){
local target=${1:-.}
# There aren't local .hg dirs everywhere only at top level so this is difficult in bash
if [ -d "$target" ] &&
[ -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(){
{
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 . "$@"
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 -R -n "$((LINES - 3))"
}
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
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}"
git submodule update
#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
}
checkout(){
if isGit "."; then
git checkout "$@";
else
echo "not a Git checkout, cannot switch to branch $*"
return 1
fi
}
commit() {
local gitcimsg=""
for x in "$@"; do
if git status -s "$x" | grep "^[?A]"; then
gitcimsg+="$x, "
fi
done
[ -z "$gitcimsg" ] && return 1
gitcimsg="${gitcimsg%, }"
gitcimsg="added $gitcimsg"
git add "$@" &&
git commit -m "$gitcimsg" "$@"
}
gitu(){
[ -n "$1" ] || { echo "ERROR: must supply arg"; return 1; }
[ "$(git diff "$@" | wc -l)" -gt 0 ] || return
git diff "$@" &&
read -r &&
git add "$@" &&
echo "committing $*" &&
git commit -m "updated $*" "$@"
}
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 ci -m "removed $*" "$@"
}
gitrename(){
git mv "$1" "$2" &&
git ci -m "renamed $1 to $2" "$1" "$2"
}
gitmv(){
git mv "$1" "$2" &&
git ci -m "moved $1 to $2" "$1" "$2"
}
gitd(){
git diff "${@:-.}"
}
# 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 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
# shellcheck disable=SC2164
popd
fi
done
echo
for submodule in $(git submodule | awk '{print $2}'); do
if [ -d "$submodule" ] && ! [ -L "$submodule" ] && ! git st "$submodule" | grep -q nothing; then
git ci -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"
}