Merge pull request #263 from HaleTom/quote-filenames
Fix #167, #202, #204, #274 and a bunch of other goodies
This commit is contained in:
@@ -6,6 +6,8 @@ env:
|
|||||||
- TEST_SHELLS=bash
|
- TEST_SHELLS=bash
|
||||||
- TEST_SHELLS=zsh
|
- TEST_SHELLS=zsh
|
||||||
|
|
||||||
|
sudo: required
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- ./test/support/travisci_deps.sh
|
- ./test/support/travisci_deps.sh
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ design() {
|
|||||||
local project=`basename $(pwd)`
|
local project=`basename $(pwd)`
|
||||||
local all_project_dirs="$design_base_dirs $design_av_dirs"
|
local all_project_dirs="$design_base_dirs $design_av_dirs"
|
||||||
# Ensure design dir contains all subdirectories
|
# Ensure design dir contains all subdirectories
|
||||||
IFS=$' \t\n'
|
local IFS=$' \t\n'
|
||||||
# Create root design dirs
|
# Create root design dirs
|
||||||
for dir in $design_ext_dirs; do mkdir -p "$root_design_dir/$dir"; done
|
for dir in $design_ext_dirs; do mkdir -p "$root_design_dir/$dir"; done
|
||||||
# Create project design dirs
|
# Create project design dirs
|
||||||
@@ -102,6 +102,5 @@ design() {
|
|||||||
printf "Invalid command.\n\n"
|
printf "Invalid command.\n\n"
|
||||||
design
|
design
|
||||||
fi
|
fi
|
||||||
unset IFS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ unalias git > /dev/null 2>&1
|
|||||||
unset -f git > /dev/null 2>&1
|
unset -f git > /dev/null 2>&1
|
||||||
|
|
||||||
# Use the full path to git to avoid infinite loop with git function
|
# Use the full path to git to avoid infinite loop with git function
|
||||||
export _git_cmd="$(\which git)"
|
export _git_cmd="$(bin_path git)"
|
||||||
# Wrap git with the 'hub' github wrapper, if installed (https://github.com/defunkt/hub)
|
# Wrap git with the 'hub' github wrapper, if installed (https://github.com/defunkt/hub)
|
||||||
if type hub > /dev/null 2>&1; then export _git_cmd="hub"; fi
|
if type hub > /dev/null 2>&1; then export _git_cmd="hub"; fi
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ __git_alias () {
|
|||||||
alias_str="$1"; cmd_prefix="$2"; cmd="$3";
|
alias_str="$1"; cmd_prefix="$2"; cmd="$3";
|
||||||
if [ $# -gt 2 ]; then
|
if [ $# -gt 2 ]; then
|
||||||
shift 3 2>/dev/null
|
shift 3 2>/dev/null
|
||||||
cmd_args=$@
|
cmd_args=("$@")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
alias $alias_str="$cmd_prefix $cmd${cmd_args:+ }${cmd_args[*]}"
|
alias $alias_str="$cmd_prefix $cmd${cmd_args:+ }${cmd_args[*]}"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ function _scmb_git_branch_shortcuts {
|
|||||||
|
|
||||||
# Use ruby to inject numbers into ls output
|
# Use ruby to inject numbers into ls output
|
||||||
ruby -e "$( cat <<EOF
|
ruby -e "$( cat <<EOF
|
||||||
output = %x($_git_cmd branch --color=always $@)
|
output = %x($_git_cmd branch --color=always "$@")
|
||||||
line_count = output.lines.to_a.size
|
line_count = output.lines.to_a.size
|
||||||
output.lines.each_with_index do |line, i|
|
output.lines.each_with_index do |line, i|
|
||||||
spaces = (line_count > 9 && i < 9 ? " " : " ")
|
spaces = (line_count > 9 && i < 9 ? " " : " ")
|
||||||
@@ -32,14 +32,12 @@ EOF
|
|||||||
)"
|
)"
|
||||||
|
|
||||||
# Set numbered file shortcut in variable
|
# Set numbered file shortcut in variable
|
||||||
local e=1
|
local e=1 IFS=$'\n'
|
||||||
IFS=$'\n'
|
|
||||||
for branch in $($_git_cmd branch "$@" | sed "s/^[* ]\{2\}//"); do
|
for branch in $($_git_cmd branch "$@" | sed "s/^[* ]\{2\}//"); do
|
||||||
export $git_env_char$e="$branch"
|
export $git_env_char$e="$branch"
|
||||||
if [ "${scmbDebug:-}" = "true" ]; then echo "Set \$$git_env_char$e => $file"; fi
|
if [ "${scmbDebug:-}" = "true" ]; then echo "Set \$$git_env_char$e => $file"; fi
|
||||||
let e++
|
let e++
|
||||||
done
|
done
|
||||||
unset IFS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__git_alias "$git_branch_alias" "_scmb_git_branch_shortcuts" ""
|
__git_alias "$git_branch_alias" "_scmb_git_branch_shortcuts" ""
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
git_status_shortcuts() {
|
git_status_shortcuts() {
|
||||||
zsh_compat # Ensure shwordsplit is on for zsh
|
zsh_compat # Ensure shwordsplit is on for zsh
|
||||||
IFS=$'\n'
|
local IFS=$'\n'
|
||||||
local git_status="$(git status --porcelain 2> /dev/null)"
|
local git_status="$(git status --porcelain 2> /dev/null)"
|
||||||
local i
|
local i
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ git_status_shortcuts() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
IFS=" "
|
local IFS=" "
|
||||||
grp_num=1
|
grp_num=1
|
||||||
for heading in 'Changes to be committed' 'Unmerged paths' 'Changes not staged for commit' 'Untracked files'; do
|
for heading in 'Changes to be committed' 'Unmerged paths' 'Changes not staged for commit' 'Untracked files'; do
|
||||||
# If no group specified as param, or specified group is current group
|
# If no group specified as param, or specified group is current group
|
||||||
@@ -114,7 +114,6 @@ git_status_shortcuts() {
|
|||||||
# so just use plain 'git status'
|
# so just use plain 'git status'
|
||||||
git status
|
git status
|
||||||
fi
|
fi
|
||||||
unset IFS
|
|
||||||
zsh_reset # Reset zsh environment to default
|
zsh_reset # Reset zsh environment to default
|
||||||
}
|
}
|
||||||
# Template function for 'git_status_shortcuts'.
|
# Template function for 'git_status_shortcuts'.
|
||||||
|
|||||||
@@ -14,3 +14,10 @@ function fail_if_not_git_repo() {
|
|||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bin_path() {
|
||||||
|
if [[ -n ${ZSH_VERSION:-} ]];
|
||||||
|
then builtin whence -cp "$1" 2> /dev/null
|
||||||
|
else builtin type -P "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
|
|
||||||
|
|
||||||
function git_index() {
|
function git_index() {
|
||||||
IFS=$'\n'
|
local IFS=$'\n'
|
||||||
if [ -z "$1" ]; then
|
if [ -z "$1" ]; then
|
||||||
# Just change to $GIT_REPO_DIR if no params given.
|
# Just change to $GIT_REPO_DIR if no params given.
|
||||||
"cd" $GIT_REPO_DIR
|
"cd" $GIT_REPO_DIR
|
||||||
@@ -103,7 +103,7 @@ function git_index() {
|
|||||||
# --------------------
|
# --------------------
|
||||||
# Go to our base path
|
# Go to our base path
|
||||||
if [ -n "$base_path" ]; then
|
if [ -n "$base_path" ]; then
|
||||||
IFS=$' \t\n'
|
local IFS=$' \t\n'
|
||||||
# evaluate ~ if necessary
|
# evaluate ~ if necessary
|
||||||
if [[ "$base_path" == "~"* ]]; then
|
if [[ "$base_path" == "~"* ]]; then
|
||||||
base_path=$(eval echo ${base_path%%/*})/${base_path#*/}
|
base_path=$(eval echo ${base_path%%/*})/${base_path#*/}
|
||||||
@@ -116,7 +116,6 @@ function git_index() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
unset IFS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_git_index_dirs_without_home() {
|
_git_index_dirs_without_home() {
|
||||||
@@ -126,12 +125,11 @@ _git_index_dirs_without_home() {
|
|||||||
# Recursively searches for git repos in $GIT_REPO_DIR
|
# Recursively searches for git repos in $GIT_REPO_DIR
|
||||||
function _find_git_repos() {
|
function _find_git_repos() {
|
||||||
# Find all unarchived projects
|
# Find all unarchived projects
|
||||||
IFS=$'\n'
|
local IFS=$'\n'
|
||||||
for repo in $(find -L "$GIT_REPO_DIR" -maxdepth 5 -name ".git" -type d \! -wholename '*/archive/*'); do
|
for repo in $(find -L "$GIT_REPO_DIR" -maxdepth 5 -name ".git" -type d \! -wholename '*/archive/*'); do
|
||||||
echo ${repo%/.git} # Return project folder, with trailing ':'
|
echo ${repo%/.git} # Return project folder, with trailing ':'
|
||||||
_find_git_submodules $repo # Detect any submodules
|
_find_git_submodules $repo # Detect any submodules
|
||||||
done
|
done
|
||||||
unset IFS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# List all submodules for a git repo, if any.
|
# List all submodules for a git repo, if any.
|
||||||
@@ -146,11 +144,10 @@ function _find_git_submodules() {
|
|||||||
function _rebuild_git_index() {
|
function _rebuild_git_index() {
|
||||||
if [ "$1" != "--silent" ]; then echo -e "== Scanning $GIT_REPO_DIR for git repos & submodules..."; fi
|
if [ "$1" != "--silent" ]; then echo -e "== Scanning $GIT_REPO_DIR for git repos & submodules..."; fi
|
||||||
# Get repos from src dir and custom dirs, then sort by basename
|
# Get repos from src dir and custom dirs, then sort by basename
|
||||||
IFS=$'\n'
|
local IFS=$'\n'
|
||||||
for repo in $(echo -e "$(_find_git_repos)\n$(echo $GIT_REPOS | sed "s/:/\\\\n/g")"); do
|
for repo in $(echo -e "$(_find_git_repos)\n$(echo $GIT_REPOS | sed "s/:/\\\\n/g")"); do
|
||||||
echo $(basename $repo | sed "s/ /_/g"):$repo
|
echo $(basename $repo | sed "s/ /_/g"):$repo
|
||||||
done | sort -t ":" -k1,1 | cut -d ":" -f2- >| "$GIT_REPO_DIR/.git_index"
|
done | sort -t ":" -k1,1 | cut -d ":" -f2- >| "$GIT_REPO_DIR/.git_index"
|
||||||
unset IFS
|
|
||||||
|
|
||||||
if [ "$1" != "--silent" ]; then
|
if [ "$1" != "--silent" ]; then
|
||||||
echo -e "===== Indexed $_bld_col$(_git_index_count)$_txt_col repos in $GIT_REPO_DIR/.git_index"
|
echo -e "===== Indexed $_bld_col$(_git_index_count)$_txt_col repos in $GIT_REPO_DIR/.git_index"
|
||||||
@@ -205,39 +202,39 @@ _git_index_update_all_branches() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local remotes merges branches
|
# zsh 5.0.2 requires local separate to assignment for arrays
|
||||||
|
local remote merge remotes merges branches
|
||||||
# Get branch configuration from .git/config
|
# Get branch configuration from .git/config
|
||||||
IFS=$'\n'
|
local IFS=$'\n'
|
||||||
for branch in $($GIT_BINARY branch 2> /dev/null | sed -e 's/.\{2\}\(.*\)/\1/'); do
|
for branch in $($GIT_BINARY branch 2> /dev/null | sed -e 's/.\{2\}\(.*\)/\1/'); do
|
||||||
# Skip '(no branch)'
|
# Skip '(no branch)'
|
||||||
if [[ "$branch" = "(no branch)" ]]; then continue; fi
|
if [[ "$branch" = "(no branch)" ]]; then continue; fi
|
||||||
|
|
||||||
local remote=$(git config --get branch.$branch.remote)
|
remote=$(git config --get "branch.$branch.remote")
|
||||||
local merge=$(git config --get branch.$branch.merge)
|
merge=$(git config --get "branch.$branch.merge")
|
||||||
|
|
||||||
# Ignore branch if remote and merge is not configured
|
# Ignore branch if remote and merge is not configured
|
||||||
if [[ -n "$remote" ]] && [[ -n "$merge" ]]; then
|
if [[ -n "$remote" ]] && [[ -n "$merge" ]]; then
|
||||||
branches=(${branches[@]} "$branch")
|
branches=("${branches[@]}" "$branch")
|
||||||
remotes=(${remotes[@]} "$remote")
|
remotes=("${remotes[@]}" "$remote")
|
||||||
# Get branch from merge ref (refs/heads/master => master)
|
# Get branch from merge ref (refs/heads/master => master)
|
||||||
merges=(${merges[@]} "$(basename $merge)")
|
merges=("${merges[@]}" "$(basename "$merge")")
|
||||||
else
|
else
|
||||||
echo "=== Skipping $branch: remote and merge refs are not configured."
|
echo "=== Skipping $branch: remote and merge refs are not configured."
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
unset IFS
|
|
||||||
|
|
||||||
# Update all remotes if there are any branches to update
|
# Update all remotes if there are any branches to update
|
||||||
if [ -n "${branches[*]}" ]; then git fetch --all 2> /dev/null; fi
|
if [ -n "${branches[*]}" ]; then git fetch --all 2> /dev/null; fi
|
||||||
|
|
||||||
local index=0
|
local index=0
|
||||||
# Iterate over branches, and update those that can be fast-forwarded
|
# Iterate over branches, and update those that can be fast-forwarded
|
||||||
for branch in ${branches[@]}; do
|
for branch in "${branches[@]}"; do
|
||||||
branch_rev="$(git rev-parse $branch)"
|
branch_rev="$(git rev-parse $branch)"
|
||||||
# Local branch can be fast-forwarded if revision is ancestor of remote revision, and not the same.
|
# Local branch can be fast-forwarded if revision is ancestor of remote revision, and not the same.
|
||||||
# (see http://stackoverflow.com/a/2934062/304706)
|
# (see http://stackoverflow.com/a/2934062/304706)
|
||||||
if [[ "$branch_rev" != "$(git rev-parse ${remotes[$index]}/${merges[$index]})" ]] && \
|
if [[ "$branch_rev" != "$(git rev-parse "${remotes[$index]}/${merges[$index]}")" ]] && \
|
||||||
[[ "$(git merge-base $branch_rev ${remotes[$index]}/${merges[$index]})" = "$branch_rev" ]]; then
|
[[ "$(git merge-base "$branch_rev" "${remotes[$index]}/${merges[$index]}")" = "$branch_rev" ]]; then
|
||||||
echo "=== Updating $branch branch in $base_path from ${remotes[$index]}/${merges[$index]}..."
|
echo "=== Updating $branch branch in $base_path from ${remotes[$index]}/${merges[$index]}..."
|
||||||
# Checkout branch if we aren't already on it.
|
# Checkout branch if we aren't already on it.
|
||||||
if [[ "$branch" != "$(parse_git_branch)" ]]; then git checkout $branch; fi
|
if [[ "$branch" != "$(parse_git_branch)" ]]; then git checkout $branch; fi
|
||||||
@@ -268,11 +265,11 @@ function _git_index_batch_cmd() {
|
|||||||
cwd="$PWD"
|
cwd="$PWD"
|
||||||
if [ -n "$1" ]; then
|
if [ -n "$1" ]; then
|
||||||
echo -e "== Running command for $_bld_col$(_git_index_count)$_txt_col repos...\n"
|
echo -e "== Running command for $_bld_col$(_git_index_count)$_txt_col repos...\n"
|
||||||
unset IFS
|
local IFS=$'\n'
|
||||||
local base_path
|
local base_path
|
||||||
for base_path in $(sed -e "s/--.*//" "$GIT_REPO_DIR/.git_index" | \grep . | sort); do
|
for base_path in $(sed -e "s/--.*//" "$GIT_REPO_DIR/.git_index" | \grep . | sort); do
|
||||||
builtin cd "$base_path"
|
builtin cd "$base_path"
|
||||||
$@
|
"$@"
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
echo "Please give a command to run for all repos. (It may be useful to write your command as a function or script.)"
|
echo "Please give a command to run for all repos. (It may be useful to write your command as a function or script.)"
|
||||||
@@ -285,8 +282,7 @@ if [ $shell = 'bash' ]; then
|
|||||||
# Bash tab completion function for git_index()
|
# Bash tab completion function for git_index()
|
||||||
function _git_index_tab_completion() {
|
function _git_index_tab_completion() {
|
||||||
_check_git_index
|
_check_git_index
|
||||||
local curw
|
local curw IFS=$'\n'
|
||||||
IFS=$'\n'
|
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
curw=${COMP_WORDS[COMP_CWORD]}
|
curw=${COMP_WORDS[COMP_CWORD]}
|
||||||
|
|
||||||
@@ -313,10 +309,9 @@ if [ $shell = 'bash' ]; then
|
|||||||
else
|
else
|
||||||
COMPREPLY=($(compgen -W '$(sed -e "s:.*/::" -e "s:$:/:" "$GIT_REPO_DIR/.git_index" | sort)' -- $curw))
|
COMPREPLY=($(compgen -W '$(sed -e "s:.*/::" -e "s:$:/:" "$GIT_REPO_DIR/.git_index" | sort)' -- $curw))
|
||||||
fi
|
fi
|
||||||
unset IFS
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
else
|
else # Zsh tab completion function for git_index()
|
||||||
function _git_index_tab_completion() {
|
function _git_index_tab_completion() {
|
||||||
typeset -A opt_args
|
typeset -A opt_args
|
||||||
local state state_descr context line
|
local state state_descr context line
|
||||||
|
|||||||
@@ -23,19 +23,19 @@ if [ "$shell_command_wrapping_enabled" = "true" ] || [ "$bash_command_wrapping_e
|
|||||||
# Define 'whence' for bash, to get the value of an alias
|
# Define 'whence' for bash, to get the value of an alias
|
||||||
type whence > /dev/null 2>&1 || function whence() { LC_MESSAGES="C" type "$@" | sed -$SED_REGEX_ARG -e "s/.*is aliased to \`//" -e "s/'$//"; }
|
type whence > /dev/null 2>&1 || function whence() { LC_MESSAGES="C" type "$@" | sed -$SED_REGEX_ARG -e "s/.*is aliased to \`//" -e "s/'$//"; }
|
||||||
local cmd=''
|
local cmd=''
|
||||||
for cmd in $(echo $scmb_wrapped_shell_commands); do
|
for cmd in $scmb_wrapped_shell_commands; do
|
||||||
if [ "${scmbDebug:-}" = "true" ]; then echo "SCMB: Wrapping $cmd..."; fi
|
if [ "${scmbDebug:-}" = "true" ]; then echo "SCMB: Wrapping $cmd..."; fi
|
||||||
|
|
||||||
# Special check for 'cd', to make sure SCM Breeze is loaded after RVM
|
# Special check for 'cd', to make sure SCM Breeze is loaded after RVM
|
||||||
if [ "$cmd" = 'cd' ]; then
|
if [ "$cmd" = 'cd' ]; then
|
||||||
if [ -e "$HOME/.rvm" ] && ! type rvm > /dev/null 2>&1; then
|
if [ -e "$HOME/.rvm" ] && ! type rvm > /dev/null 2>&1; then
|
||||||
echo -e "\033[0;31mSCM Breeze must be loaded \033[1;31mafter\033[0;31m RVM, otherwise there will be a conflict when RVM wraps the 'cd' command.\033[0m"
|
echo -e "\\033[0;31mSCM Breeze must be loaded \\033[1;31mafter\\033[0;31m RVM, otherwise there will be a conflict when RVM wraps the 'cd' command.\\033[0m"
|
||||||
echo -e "\033[0;31mPlease move the line that loads SCM Breeze to the bottom of your ~/.bashrc\033[0m"
|
echo -e "\\033[0;31mPlease move the line that loads SCM Breeze to the bottom of your ~/.bashrc\\033[0m"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$(LC_MESSAGES="C" type $cmd 2>&1)" in
|
case "$(LC_MESSAGES="C" type "$cmd" 2>&1)" in
|
||||||
|
|
||||||
# Don't do anything if command already aliased, or not found.
|
# Don't do anything if command already aliased, or not found.
|
||||||
*'exec_scmb_expand_args'*)
|
*'exec_scmb_expand_args'*)
|
||||||
@@ -49,10 +49,10 @@ if [ "$shell_command_wrapping_enabled" = "true" ] || [ "$bash_command_wrapping_e
|
|||||||
# Store original alias
|
# Store original alias
|
||||||
local original_alias="$(whence $cmd)"
|
local original_alias="$(whence $cmd)"
|
||||||
# Remove alias, so that we can find binary
|
# Remove alias, so that we can find binary
|
||||||
unalias $cmd
|
unalias "$cmd"
|
||||||
|
|
||||||
# Detect original $cmd type, and escape
|
# Detect original $cmd type, and escape
|
||||||
case "$(LC_MESSAGES="C" type $cmd 2>&1)" in
|
case "$(LC_MESSAGES="C" type "$cmd" 2>&1)" in
|
||||||
# Escape shell builtins with 'builtin'
|
# Escape shell builtins with 'builtin'
|
||||||
*'is a shell builtin'*) local escaped_cmd="builtin $cmd";;
|
*'is a shell builtin'*) local escaped_cmd="builtin $cmd";;
|
||||||
# Get full path for files with 'find_binary' function
|
# Get full path for files with 'find_binary' function
|
||||||
@@ -67,9 +67,9 @@ if [ "$shell_command_wrapping_enabled" = "true" ] || [ "$bash_command_wrapping_e
|
|||||||
*'is a'*'function'*)
|
*'is a'*'function'*)
|
||||||
if [ "${scmbDebug:-}" = "true" ]; then echo "SCMB: $cmd is a function"; fi
|
if [ "${scmbDebug:-}" = "true" ]; then echo "SCMB: $cmd is a function"; fi
|
||||||
# Copy old function into new name
|
# Copy old function into new name
|
||||||
eval "$(declare -f $cmd | sed -$SED_REGEX_ARG "s/^$cmd \(\)/__original_$cmd ()/")"
|
eval "$(declare -f "$cmd" | sed -"$SED_REGEX_ARG" "s/^$cmd \\(\\)/__original_$cmd ()/")"
|
||||||
# Remove function
|
# Remove function
|
||||||
unset -f $cmd
|
unset -f "$cmd"
|
||||||
# Create function that wraps old function
|
# Create function that wraps old function
|
||||||
eval "${cmd}(){ exec_scmb_expand_args __original_${cmd} \"\$@\"; }";;
|
eval "${cmd}(){ exec_scmb_expand_args __original_${cmd} \"\$@\"; }";;
|
||||||
|
|
||||||
@@ -100,64 +100,85 @@ if ! ls --color=auto > /dev/null 2>&1; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Test if readlink supports -f option, otherwise use perl (a bit slower)
|
# Test if readlink supports -f option, otherwise use perl (a bit slower)
|
||||||
if ! readlink -f > /dev/null 2>&1; then
|
if ! readlink -f / > /dev/null 2>&1; then
|
||||||
_abs_path_command='perl -e "use Cwd "abs_path"; print abs_path(shift)"'
|
_abs_path_command=(perl -e 'use Cwd abs_path; print abs_path(shift)')
|
||||||
else
|
else
|
||||||
_abs_path_command="readlink -f"
|
_abs_path_command=(readlink -f)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Function wrapper around 'll'
|
# Function wrapper around 'll'
|
||||||
# Adds numbered shortcuts to output of ls -l, just like 'git status'
|
# Adds numbered shortcuts to output of ls -l, just like 'git status'
|
||||||
if [ "$shell_ls_aliases_enabled" = "true" ] && which ruby > /dev/null 2>&1; then
|
if [ "$shell_ls_aliases_enabled" = "true" ] && builtin command -v ruby > /dev/null 2>&1; then
|
||||||
unalias ll > /dev/null 2>&1; unset -f ll > /dev/null 2>&1
|
unalias ll > /dev/null 2>&1; unset -f ll > /dev/null 2>&1
|
||||||
function ls_with_file_shortcuts {
|
function ls_with_file_shortcuts {
|
||||||
local ll_output
|
local ll_output
|
||||||
|
local ll_command # Ensure sort ordering of the two invocations is the same
|
||||||
if [ "$_ls_bsd" != "BSD" ]; then
|
if [ "$_ls_bsd" != "BSD" ]; then
|
||||||
ll_output="$(\ls -lhv --group-directories-first --color "$@")"
|
ll_command=(\ls -hv --group-directories-first)
|
||||||
|
ll_output="$("${ll_command[@]}" -l --color "$@")"
|
||||||
else
|
else
|
||||||
ll_output="$(CLICOLOR_FORCE=1 \ls -l -G "$@")"
|
ll_command=(\ls)
|
||||||
|
ll_output="$(CLICOLOR_FORCE=1 "${ll_command[@]}" -lG "$@")"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $shell == "zsh" ]]; then
|
if [[ $shell == "zsh" ]]; then
|
||||||
# Ensure sh_word_split is on
|
# Ensure sh_word_split is on
|
||||||
if setopt | grep -q shwordsplit; then SHWORDSPLIT_ON=true; fi
|
[[ -o shwordsplit ]] && SHWORDSPLIT_ON=true
|
||||||
setopt shwordsplit
|
setopt shwordsplit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Parse path from args
|
# Get the directory that `ls` is being run relative to.
|
||||||
IFS=$'\n'
|
# Only allow one directory to avoid incorrect $e# variables when listing
|
||||||
for arg in $@; do
|
# multiple directories (issue #274)
|
||||||
if [ -d "$arg" ]; then local rel_path="${arg%/}"; fi
|
local IFS=$'\n'
|
||||||
|
local rel_path
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [[ -e $arg ]]; then # Path rather than option to ls
|
||||||
|
if [[ -z $rel_path ]]; then # We are seeing our first pathname
|
||||||
|
if [[ -d $arg ]]; then # It's a directory
|
||||||
|
rel_path=$arg
|
||||||
|
else # It's a file, expand the current directory
|
||||||
|
rel_path=.
|
||||||
|
fi
|
||||||
|
elif [[ -d $arg || ( -f $arg && $rel_path != . ) ]]; then
|
||||||
|
if [[ -f $arg ]]; then arg=$PWD; fi # Get directory for current argument
|
||||||
|
# We've already seen a different directory. Quit to avoid damage (issue #274)
|
||||||
|
printf 'scm_breeze: Cannot list relative to both directories:\n %s\n %s\n' "$arg" "$rel_path" >&2
|
||||||
|
printf 'Currently only listing a single directory is supported. See issue #274.\n' >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
unset IFS
|
rel_path=$("${_abs_path_command[@]}" ${rel_path:-$PWD})
|
||||||
|
|
||||||
# Replace user/group with user symbol, if defined at ~/.user_sym
|
# Replace user/group with user symbol, if defined at ~/.user_sym
|
||||||
# Before : -rw-rw-r-- 1 ndbroadbent ndbroadbent 1.1K Sep 19 21:39 scm_breeze.sh
|
# Before : -rw-rw-r-- 1 ndbroadbent ndbroadbent 1.1K Sep 19 21:39 scm_breeze.sh
|
||||||
# After : -rw-rw-r-- 1 𝐍 𝐍 1.1K Sep 19 21:39 scm_breeze.sh
|
# After : -rw-rw-r-- 1 𝐍 𝐍 1.1K Sep 19 21:39 scm_breeze.sh
|
||||||
if [ -e $HOME/.user_sym ]; then
|
if [ -e "$HOME"/.user_sym ]; then
|
||||||
# Little bit of ruby golf to rejustify the user/group/size columns after replacement
|
# Little bit of ruby golf to rejustify the user/group/size columns after replacement
|
||||||
|
# TODO(ghthor): Convert this to a cat <<EOF to improve readibility
|
||||||
function rejustify_ls_columns(){
|
function rejustify_ls_columns(){
|
||||||
ruby -e "o=STDIN.read;re=/^(([^ ]* +){2})(([^ ]* +){3})/;\
|
ruby -e "o=STDIN.read;re=/^(([^ ]* +){2})(([^ ]* +){3})/;\
|
||||||
u,g,s=o.lines.map{|l|l[re,3]}.compact.map(&:split).transpose.map{|a|a.map(&:size).max+1};\
|
u,g,s=o.lines.map{|l|l[re,3]}.compact.map(&:split).transpose.map{|a|a.map(&:size).max+1};\
|
||||||
puts o.lines.map{|l|l.sub(re){|m|\"%s%-#{u}s %-#{g}s%#{s}s \"%[\$1,*\$3.split]}}"
|
puts o.lines.map{|l|l.sub(re){|m|\"%s%-#{u}s %-#{g}s%#{s}s \"%[\$1,*\$3.split]}}"
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ -f "$HOME/.user_sym" ]; then
|
|
||||||
local USER_SYM=$(/bin/cat $HOME/.user_sym)
|
local USER_SYM=$(/bin/cat $HOME/.user_sym)
|
||||||
if [ -f "$HOME/.staff_sym" ]; then
|
if [ -f "$HOME/.staff_sym" ]; then
|
||||||
local STAFF_SYM=$(/bin/cat $HOME/.staff_sym)
|
local STAFF_SYM=$(/bin/cat $HOME/.staff_sym)
|
||||||
ll_output=$(echo "$ll_output" | \
|
ll_output=$(echo "$ll_output" | \
|
||||||
\sed -$SED_REGEX_ARG "s/ $USER staff/ $USER_SYM $STAFF_SYM /g")
|
\sed -$SED_REGEX_ARG "s/ $USER staff/ $USER_SYM $STAFF_SYM /g" | \
|
||||||
fi
|
rejustify_ls_columns)
|
||||||
|
else
|
||||||
ll_output=$(echo "$ll_output" | \
|
ll_output=$(echo "$ll_output" | \
|
||||||
\sed -$SED_REGEX_ARG "s/ $USER/ $USER_SYM /g")
|
\sed -$SED_REGEX_ARG "s/ $USER/ $USER_SYM /g" | \
|
||||||
fi
|
rejustify_ls_columns)
|
||||||
|
fi
|
||||||
ll_output=$(echo "$ll_output" | rejustify_ls_columns)
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Bail if there are two many lines to process
|
||||||
if [ "$(echo "$ll_output" | wc -l)" -gt "50" ]; then
|
if [ "$(echo "$ll_output" | wc -l)" -gt "50" ]; then
|
||||||
echo -e "\033[33mToo many files to create shortcuts. Running plain ll command...\033[0m"
|
echo -e '\033[33mToo many files to create shortcuts. Running plain ll command...\033[0m' >&2
|
||||||
echo "$ll_output"
|
echo "$ll_output"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
@@ -180,23 +201,26 @@ EOF
|
|||||||
local ll_files=''
|
local ll_files=''
|
||||||
local file=''
|
local file=''
|
||||||
|
|
||||||
|
# XXX FIXME XXX
|
||||||
|
# There is a race condition here: If a file is removed between the above
|
||||||
|
# and this second call of `ls` then the $e# variables can refer to the
|
||||||
|
# wrong files.
|
||||||
if [ -z $_ls_bsd ]; then
|
if [ -z $_ls_bsd ]; then
|
||||||
ll_files="$(\ls -v --group-directories-first --color=never "$@")"
|
ll_files="$(QUOTING_STYLE=literal "${ll_command[@]}" --color=never "$@")"
|
||||||
else
|
else
|
||||||
ll_files="$(\ls "$@")"
|
ll_files="$("${ll_command[@]}" "$@")"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
IFS=$'\n'
|
local IFS=$'\n'
|
||||||
for file in $ll_files; do
|
for file in $ll_files; do
|
||||||
if [ -n "$rel_path" ]; then file="$rel_path/$file"; fi
|
file=$rel_path/$file
|
||||||
export $git_env_char$e="$(eval $_abs_path_command \"${file//\"/\\\"}\")"
|
export $git_env_char$e=$("${_abs_path_command[@]}" "$file")
|
||||||
if [ "${scmbDebug:-}" = "true" ]; then echo "Set \$$git_env_char$e => $file"; fi
|
if [[ ${scmbDebug:-} = true ]]; then echo "Set \$$git_env_char$e => $file"; fi
|
||||||
let e++
|
let e++
|
||||||
done
|
done
|
||||||
unset IFS
|
|
||||||
|
|
||||||
# Turn off shwordsplit unless it was on previously
|
# Turn off shwordsplit unless it was on previously
|
||||||
if [[ $shell == "zsh" ]] && [ -z "$SHWORDSPLIT_ON" ]; then unsetopt shwordsplit; fi
|
if [[ $shell == "zsh" && -z $SHWORDSPLIT_ON ]]; then unsetopt shwordsplit; fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Setup aliases
|
# Setup aliases
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ git_status_shortcuts() {
|
|||||||
zsh_compat # Ensure shwordsplit is on for zsh
|
zsh_compat # Ensure shwordsplit is on for zsh
|
||||||
git_clear_vars
|
git_clear_vars
|
||||||
# Run ruby script, store output
|
# Run ruby script, store output
|
||||||
local cmd_output="$(/usr/bin/env ruby "$scmbDir/lib/git/status_shortcuts.rb" $@)"
|
local cmd_output="$(/usr/bin/env ruby "$scmbDir/lib/git/status_shortcuts.rb" "$@")"
|
||||||
# Print debug information if $scmbDebug = "true"
|
# Print debug information if $scmbDebug = "true"
|
||||||
if [ "${scmbDebug:-}" = "true" ]; then
|
if [ "${scmbDebug:-}" = "true" ]; then
|
||||||
printf "status_shortcuts.rb output => \n$cmd_output\n------------------------\n"
|
printf "status_shortcuts.rb output => \n$cmd_output\n------------------------\n"
|
||||||
@@ -36,14 +36,13 @@ git_status_shortcuts() {
|
|||||||
files="$(echo "$cmd_output" | \grep '@@filelist@@::' | sed 's%@@filelist@@::%%g')"
|
files="$(echo "$cmd_output" | \grep '@@filelist@@::' | sed 's%@@filelist@@::%%g')"
|
||||||
if [ "${scmbDebug:-}" = "true" ]; then echo "filelist => $files"; fi
|
if [ "${scmbDebug:-}" = "true" ]; then echo "filelist => $files"; fi
|
||||||
# Export numbered env variables for each file
|
# Export numbered env variables for each file
|
||||||
IFS="|"
|
local IFS="|"
|
||||||
local e=1
|
local e=1
|
||||||
for file in $files; do
|
for file in $files; do
|
||||||
export $git_env_char$e="$file"
|
export $git_env_char$e="$file"
|
||||||
if [ "${scmbDebug:-}" = "true" ]; then echo "Set \$$git_env_char$e => $file"; fi
|
if [ "${scmbDebug:-}" = "true" ]; then echo "Set \$$git_env_char$e => $file"; fi
|
||||||
let e++
|
let e++
|
||||||
done
|
done
|
||||||
unset IFS
|
|
||||||
|
|
||||||
if [ "${scmbDebug:-}" = "true" ]; then echo "------------------------"; fi
|
if [ "${scmbDebug:-}" = "true" ]; then echo "------------------------"; fi
|
||||||
# Print status
|
# Print status
|
||||||
@@ -80,10 +79,11 @@ git_add_shortcuts() {
|
|||||||
git_silent_add_shortcuts() {
|
git_silent_add_shortcuts() {
|
||||||
if [ -n "$1" ]; then
|
if [ -n "$1" ]; then
|
||||||
# Expand args and process resulting set of files.
|
# Expand args and process resulting set of files.
|
||||||
IFS=$'\t'
|
local args
|
||||||
for file in $(scmb_expand_args "$@"); do
|
eval args="$(scmb_expand_args "$@")" # populate $args array
|
||||||
|
for file in "${args[@]}"; do
|
||||||
# Use 'git rm' if file doesn't exist and 'ga_auto_remove' is enabled.
|
# Use 'git rm' if file doesn't exist and 'ga_auto_remove' is enabled.
|
||||||
if [[ $ga_auto_remove == "yes" ]] && ! [ -e "$file" ]; then
|
if [[ $ga_auto_remove = yes && ! -e $file ]]; then
|
||||||
echo -n "# "
|
echo -n "# "
|
||||||
git rm "$file"
|
git rm "$file"
|
||||||
else
|
else
|
||||||
@@ -91,7 +91,6 @@ git_silent_add_shortcuts() {
|
|||||||
echo -e "# Added '$file'"
|
echo -e "# Added '$file'"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
unset IFS
|
|
||||||
echo "#"
|
echo "#"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -100,18 +99,18 @@ git_silent_add_shortcuts() {
|
|||||||
# and exports numbered environment variables for each file.
|
# and exports numbered environment variables for each file.
|
||||||
git_show_affected_files(){
|
git_show_affected_files(){
|
||||||
fail_if_not_git_repo || return 1
|
fail_if_not_git_repo || return 1
|
||||||
f=0 # File count
|
local f=0 # File count
|
||||||
# Show colored revision and commit message
|
# Show colored revision and commit message
|
||||||
echo -n "# "; git show --oneline --name-only $@ | head -n1; echo "# "
|
echo -n "# "; git show --oneline --name-only "$@" | head -n1; echo "# "
|
||||||
for file in $(git show --pretty="format:" --name-only $@ | \grep -v '^$'); do
|
for file in $(git show --pretty="format:" --name-only "$@" | \grep -v '^$'); do
|
||||||
let f++
|
let f++
|
||||||
export $git_env_char$f=$file # Export numbered variable.
|
export $git_env_char$f=$file # Export numbered variable.
|
||||||
echo -e "# \033[2;37m[\033[0m$f\033[2;37m]\033[0m $file"
|
echo -e "# \033[2;37m[\033[0m$f\033[2;37m]\033[0m $file"
|
||||||
done; echo "# "
|
done; echo "# "
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Allows expansion of numbered shortcuts, ranges of shortcuts, or standard paths.
|
# Allows expansion of numbered shortcuts, ranges of shortcuts, or standard paths.
|
||||||
|
# Return a string which can be `eval`ed like: eval args="$(scmb_expand_args "$@")"
|
||||||
# Numbered shortcut variables are produced by various commands, such as:
|
# Numbered shortcut variables are produced by various commands, such as:
|
||||||
# * git_status_shortcuts() - git status implementation
|
# * git_status_shortcuts() - git status implementation
|
||||||
# * git_show_affected_files() - shows files affected by a given SHA1, etc.
|
# * git_show_affected_files() - shows files affected by a given SHA1, etc.
|
||||||
@@ -122,43 +121,52 @@ scmb_expand_args() {
|
|||||||
shift
|
shift
|
||||||
fi
|
fi
|
||||||
|
|
||||||
first=1
|
local args
|
||||||
OLDIFS="$IFS"; IFS=" " # We need to split on spaces to loop over expanded range
|
args=() # initially empty array. zsh 5.0.2 from Ubuntu 14.04 requires this to be separated
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
if [[ "$arg" =~ ^[0-9]{0,4}$ ]] ; then # Substitute $e{*} variables for any integers
|
if [[ "$arg" =~ ^[0-9]{0,4}$ ]] ; then # Substitute $e{*} variables for any integers
|
||||||
if [ "$first" -eq 1 ]; then first=0; else printf '\t'; fi
|
|
||||||
if [ -e "$arg" ]; then
|
if [ -e "$arg" ]; then
|
||||||
# Don't expand files or directories with numeric names
|
# Don't expand files or directories with numeric names
|
||||||
printf '%s' "$arg"
|
args+=("$arg")
|
||||||
else
|
else
|
||||||
_print_path "$relative" "$git_env_char$arg"
|
args+=("$(_print_path "$relative" "$git_env_char$arg")")
|
||||||
fi
|
fi
|
||||||
elif [[ "$arg" =~ ^[0-9]+-[0-9]+$ ]]; then # Expand ranges into $e{*} variables
|
elif [[ "$arg" =~ ^[0-9]+-[0-9]+$ ]]; then # Expand ranges into $e{*} variables
|
||||||
|
|
||||||
for i in $(eval echo {${arg/-/..}}); do
|
for i in $(eval echo {${arg/-/..}}); do
|
||||||
if [ "$first" -eq 1 ]; then first=0; else printf '\t'; fi
|
args+=("$(_print_path "$relative" "$git_env_char$i")")
|
||||||
_print_path "$relative" "$git_env_char$i"
|
|
||||||
done
|
done
|
||||||
else # Otherwise, treat $arg as a normal string.
|
else # Otherwise, treat $arg as a normal string.
|
||||||
if [ "$first" -eq 1 ]; then first=0; else printf '\t'; fi
|
args+=("$arg")
|
||||||
printf '%s' "$arg"
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
IFS="$OLDIFS"
|
|
||||||
|
# "declare -p" with zsh 5.0.2 on Ubuntu 14.04 creates a string that it cannot process:
|
||||||
|
# typeset -a args args=(one three six)
|
||||||
|
# There should be a ; between the two "args" tokens
|
||||||
|
# "declare -p" with bash 4.3.11(1) on Ubuntu 14.04 creates a string like:
|
||||||
|
# declare -a a='([0]="a" [1]="b c" [2]="d")'
|
||||||
|
# The RHS of this string is incompatible with zsh 5.0.2 and "eval args="
|
||||||
|
|
||||||
|
# Generate a quoted array string to assign to "eval args="
|
||||||
|
echo "( $(token_quote "${args[@]}") )"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Expand a variable (named by $2) into a (possibly relative) pathname
|
||||||
_print_path() {
|
_print_path() {
|
||||||
if [ "$1" = 1 ]; then
|
local pathname
|
||||||
eval printf '%s' "\"\$$2\"" | sed -e "s%$(pwd)/%%" | awk '{printf("%s", $0)}'
|
pathname=$(eval printf '%s' "\"\${$2}\"")
|
||||||
else
|
if [ "$1" = 1 ]; then # print relative
|
||||||
eval printf '%s' "\"\$$2\""
|
pathname=${pathname#$PWD/} # Remove $PWD from beginning of the path
|
||||||
fi
|
fi
|
||||||
|
printf '%s' "$pathname"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Execute a command with expanded args, e.g. Delete files 6 to 12: $ ge rm 6-12
|
# Execute a command with expanded args, e.g. Delete files 6 to 12: $ ge rm 6-12
|
||||||
# Fails if command is a number or range (probably not worth fixing)
|
# Fails if command is a number or range (probably not worth fixing)
|
||||||
exec_scmb_expand_args() {
|
exec_scmb_expand_args() {
|
||||||
eval "$(scmb_expand_args "$@" | sed -e "s/\([][|;()<>^ \"'&]\)/"'\\\1/g')"
|
local args
|
||||||
|
eval "args=$(scmb_expand_args "$@")" # populate $args array
|
||||||
|
_safe_eval "${args[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Clear numbered env variables
|
# Clear numbered env variables
|
||||||
@@ -180,13 +188,13 @@ git_clear_vars() {
|
|||||||
_git_resolve_merge_conflict() {
|
_git_resolve_merge_conflict() {
|
||||||
if [ -n "$2" ]; then
|
if [ -n "$2" ]; then
|
||||||
# Expand args and process resulting set of files.
|
# Expand args and process resulting set of files.
|
||||||
IFS=$'\t'
|
local args
|
||||||
for file in $(scmb_expand_args "${@:2}"); do
|
eval "args=$(scmb_expand_args "$@")" # populate $args array
|
||||||
|
for file in "${args[@]:2}"; do
|
||||||
git checkout "--$1""s" "$file" # "--$1""s" is expanded to --ours or --theirs
|
git checkout "--$1""s" "$file" # "--$1""s" is expanded to --ours or --theirs
|
||||||
git add "$file"
|
git add "$file"
|
||||||
echo -e "# Added $1 version of '$file'"
|
echo -e "# Added $1 version of '$file'"
|
||||||
done
|
done
|
||||||
unset IFS
|
|
||||||
echo -e "# -- If you have finished resolving conflicts, commit the resolutions with 'git commit'"
|
echo -e "# -- If you have finished resolving conflicts, commit the resolutions with 'git commit'"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,9 @@ git_remove_history() {
|
|||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
# Remove all paths passed as arguments from the history of the repo
|
# Remove all paths passed as arguments from the history of the repo
|
||||||
files=$@
|
local files
|
||||||
$_git_cmd filter-branch --index-filter "$_git_cmd rm -rf --cached --ignore-unmatch $files" HEAD
|
files=("$@")
|
||||||
|
$_git_cmd filter-branch --index-filter "$_git_cmd rm -rf --cached --ignore-unmatch ${files[*]}" HEAD
|
||||||
# Remove the temporary history git-filter-branch otherwise leaves behind for a long time
|
# Remove the temporary history git-filter-branch otherwise leaves behind for a long time
|
||||||
rm -rf .git/refs/original/ && $_git_cmd reflog expire --all && $_git_cmd gc --aggressive --prune
|
rm -rf .git/refs/original/ && $_git_cmd reflog expire --all && $_git_cmd gc --aggressive --prune
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,48 @@ _alias() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Quote the contents of "$@"
|
||||||
|
function token_quote {
|
||||||
|
# Older versions of {ba,z}sh don't support the built-in quoting, so fall back to printf %q
|
||||||
|
local quoted
|
||||||
|
quoted=() # Assign separately for zsh 5.0.2 of Ubuntu 14.04
|
||||||
|
for token; do
|
||||||
|
quoted+=( "$(printf '%q' "$token")" )
|
||||||
|
done
|
||||||
|
printf '%s\n' "${quoted[*]}"
|
||||||
|
|
||||||
|
# Keep this code for use when minimum versions of {ba,z}sh can be increased.
|
||||||
|
# See https://github.com/scmbreeze/scm_breeze/issues/260
|
||||||
|
#
|
||||||
|
# if [[ $shell = bash ]]; then
|
||||||
|
# # ${parameter@operator} where parameter is ${@} and operator is 'Q'
|
||||||
|
# # https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
|
||||||
|
# eval "${@@Q}"
|
||||||
|
# else # zsh
|
||||||
|
# # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
|
||||||
|
# eval "${(q-)@}"
|
||||||
|
# fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Quote "$@" before `eval` to prevent arbitrary code execution.
|
||||||
|
# Eg, the following will run `date`:
|
||||||
|
# evil() { eval "$@"; }; evil "echo" "foo;date"
|
||||||
|
function _safe_eval() {
|
||||||
|
eval $(token_quote "$@")
|
||||||
|
|
||||||
|
# Keep this code for use when minimum versions of {ba,z}sh can be increased.
|
||||||
|
# See https://github.com/scmbreeze/scm_breeze/issues/260
|
||||||
|
#
|
||||||
|
# if [[ $shell = bash ]]; then
|
||||||
|
# # ${parameter@operator} where parameter is ${@} and operator is 'Q'
|
||||||
|
# # https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
|
||||||
|
# eval "${@@Q}"
|
||||||
|
# else # zsh
|
||||||
|
# # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
|
||||||
|
# eval "${(q-)@}"
|
||||||
|
# fi
|
||||||
|
}
|
||||||
|
|
||||||
find_binary(){
|
find_binary(){
|
||||||
if [ $shell = "zsh" ]; then
|
if [ $shell = "zsh" ]; then
|
||||||
builtin type -p "$1" | sed "s/$1 is //" | head -1
|
builtin type -p "$1" | sed "s/$1 is //" | head -1
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ failed=false
|
|||||||
if [ -z "$TEST_SHELLS" ]; then
|
if [ -z "$TEST_SHELLS" ]; then
|
||||||
TEST_SHELLS="bash zsh"
|
TEST_SHELLS="bash zsh"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "== Will run all tests with following shells: ${TEST_SHELLS}"
|
echo "== Will run all tests with following shells: ${TEST_SHELLS}"
|
||||||
|
|
||||||
|
cd -P -- "${0%/*}" # Change to directory this script lives in
|
||||||
for test in $(find test/lib -name *_test.sh); do
|
for test in $(find test/lib -name *_test.sh); do
|
||||||
for shell in $TEST_SHELLS; do
|
for shell in $TEST_SHELLS; do
|
||||||
echo "== Running tests with [$shell]: $test"
|
echo "== Running tests with [$shell]: $test"
|
||||||
|
|||||||
@@ -68,13 +68,12 @@ EOF
|
|||||||
done
|
done
|
||||||
|
|
||||||
# Setup some custom repos outside the main repo dir
|
# Setup some custom repos outside the main repo dir
|
||||||
IFS=":"
|
local IFS=":"
|
||||||
for dir in $GIT_REPOS; do
|
for dir in $GIT_REPOS; do
|
||||||
mkdir -p $dir
|
mkdir -p $dir
|
||||||
cd $dir
|
cd $dir
|
||||||
git init
|
git init
|
||||||
done
|
done
|
||||||
unset IFS
|
|
||||||
|
|
||||||
verboseGitCommands
|
verboseGitCommands
|
||||||
|
|
||||||
@@ -83,9 +82,8 @@ EOF
|
|||||||
|
|
||||||
oneTimeTearDown() {
|
oneTimeTearDown() {
|
||||||
rm -rf "${GIT_REPO_DIR}"
|
rm -rf "${GIT_REPO_DIR}"
|
||||||
IFS=":"
|
local IFS=":"
|
||||||
for dir in $GIT_REPOS; do rm -rf $dir; done
|
for dir in $GIT_REPOS; do rm -rf $dir; done
|
||||||
unset IFS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureIndex() {
|
ensureIndex() {
|
||||||
|
|||||||
@@ -21,13 +21,6 @@ fi
|
|||||||
source "$scmbDir/test/support/test_helper.sh"
|
source "$scmbDir/test/support/test_helper.sh"
|
||||||
source "$scmbDir/lib/scm_breeze.sh"
|
source "$scmbDir/lib/scm_breeze.sh"
|
||||||
|
|
||||||
bin_path() {
|
|
||||||
if [ -n "${ZSH_VERSION:-}" ];
|
|
||||||
then where "$@" | tail -1
|
|
||||||
else which "$@"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
oneTimeSetUp() {
|
oneTimeSetUp() {
|
||||||
@@ -38,7 +31,7 @@ oneTimeSetUp() {
|
|||||||
alias rvm="test" # Ensure tests run if RVM isn't loaded but $HOME/.rvm is present
|
alias rvm="test" # Ensure tests run if RVM isn't loaded but $HOME/.rvm is present
|
||||||
|
|
||||||
# Test functions
|
# Test functions
|
||||||
function ln() { ln $@; }
|
function ln() { ln "$@"; }
|
||||||
|
|
||||||
# Before aliasing, get original locations so we can compare them in the test
|
# Before aliasing, get original locations so we can compare them in the test
|
||||||
unalias mv rm sed cat 2>/dev/null
|
unalias mv rm sed cat 2>/dev/null
|
||||||
@@ -69,6 +62,15 @@ assertAliasEquals(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
# Setup and tear down
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
setUp() {
|
||||||
|
unset QUOTING_STYLE # Use default quoting style for ls
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
# Unit tests
|
# Unit tests
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
@@ -93,18 +95,18 @@ test_ls_with_file_shortcuts() {
|
|||||||
# full physical path to be absolutely certain when doing comparisons later,
|
# full physical path to be absolutely certain when doing comparisons later,
|
||||||
# because thats how the Ruby status_shortcuts.rb script is going to obtain
|
# because thats how the Ruby status_shortcuts.rb script is going to obtain
|
||||||
# them.
|
# them.
|
||||||
cd $TEST_DIR
|
cd "$TEST_DIR"
|
||||||
TEST_DIR=$(pwd -P)
|
TEST_DIR=$(pwd -P)
|
||||||
|
|
||||||
touch 'test file' 'test_file'
|
touch 'test file' 'test_file'
|
||||||
mkdir -p "a [b]" 'a "b"' "a 'b'"
|
mkdir -p "a [b]" 'a "b"' "a 'b'"
|
||||||
touch "a \"b\"/c"
|
touch 'a "b"/c'
|
||||||
|
|
||||||
# Run command in shell, load output from temp file into variable
|
# Run command in shell, load output from temp file into variable
|
||||||
# (This is needed so that env variables are exported in the current shell)
|
# (This is needed so that env variables are exported in the current shell)
|
||||||
temp_file=$(mktemp -t scm_breeze.XXXXXXXXXX)
|
temp_file=$(mktemp -t scm_breeze.XXXXXXXXXX)
|
||||||
ls_with_file_shortcuts > $temp_file
|
ls_with_file_shortcuts > "$temp_file"
|
||||||
ls_output=$(<$temp_file strip_colors)
|
ls_output=$(<"$temp_file" strip_colors)
|
||||||
|
|
||||||
# Compare as fixed strings (F), instead of normal grep behavior
|
# Compare as fixed strings (F), instead of normal grep behavior
|
||||||
assertIncludes "$ls_output" '[1] a "b"' F
|
assertIncludes "$ls_output" '[1] a "b"' F
|
||||||
@@ -125,10 +127,24 @@ test_ls_with_file_shortcuts() {
|
|||||||
ls_output=$(<$temp_file strip_colors)
|
ls_output=$(<$temp_file strip_colors)
|
||||||
assertIncludes "$ls_output" '[1] c' F
|
assertIncludes "$ls_output" '[1] c' F
|
||||||
# Test that env variable is set correctly
|
# Test that env variable is set correctly
|
||||||
assertEquals "$TEST_DIR/a \"b\"/c" "$e1"
|
assertEquals "$TEST_DIR/"'a "b"/c' "$e1"
|
||||||
# Test arg with no quotes
|
# Test arg with no quotes
|
||||||
ls_output=$(ls_with_file_shortcuts a\ \"b\" | strip_colors)
|
ls_output=$(ls_with_file_shortcuts a\ \"b\" | strip_colors)
|
||||||
assertIncludes "$ls_output" '[1] c' F
|
assertIncludes "$ls_output" '[1] c' F
|
||||||
|
|
||||||
|
# Listing two directories fails (see issue #275)
|
||||||
|
mkdir 1 2
|
||||||
|
touch 1/file
|
||||||
|
assertFalse 'Only one directory supported' 'ls_with_file_shortcuts 1 2'
|
||||||
|
assertFalse 'Fails on <directory> <file>' 'ls_with_file_shortcuts 1 test_file'
|
||||||
|
assertFalse 'Fails on <file> <directory>' 'ls_with_file_shortcuts test_file 1'
|
||||||
|
assertFalse 'Fails on <directory> <directory>/<file>' 'ls_with_file_shortcuts 1 1/file'
|
||||||
|
|
||||||
|
# Files under the root directory
|
||||||
|
assertTrue 'Shortcuts under /' 'ls_with_file_shortcuts / > /dev/null && [[ $e1 =~ ^/[^/]+$ ]]'
|
||||||
|
|
||||||
|
cd -
|
||||||
|
rm -r "$TEST_DIR" "$temp_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
# load and run shUnit2
|
# load and run shUnit2
|
||||||
|
|||||||
@@ -49,21 +49,72 @@ setupTestRepo() {
|
|||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
test_scmb_expand_args() {
|
test_scmb_expand_args() {
|
||||||
local e1="one"; local e2="two"; local e3="three"; local e4="four"; local e5="five"; local e6="six"; local e7="seven"
|
local e1="one" e2="two" e3="three" e4="four" e5="five" e6="six" e7='$dollar' e8='two words'
|
||||||
local error="Args not expanded correctly"
|
local error="Args not expanded correctly"
|
||||||
assertEquals "$error" "$(printf 'one\tthree\tseven')" "$(scmb_expand_args 1 3 7)"
|
assertEquals "$error" 'one three six' \
|
||||||
assertEquals "$error" "$(printf 'one\ttwo\tthree\tsix')" "$(scmb_expand_args 1-3 6)"
|
"$(eval args="$(scmb_expand_args 1 3 6)"; token_quote "${args[@]}")"
|
||||||
assertEquals "$error" "$(printf 'seven\ttwo\tthree\tfour\tfive\tone')" "$(scmb_expand_args seven 2-5 1)"
|
assertEquals "$error" 'one two three five' \
|
||||||
|
"$(eval args="$(scmb_expand_args 1-3 5)"; token_quote "${args[@]}")"
|
||||||
|
assertEquals "$error" '\$dollar two three four one' \
|
||||||
|
"$(eval args="$(scmb_expand_args 7 2-4 1)"; token_quote "${args[@]}")"
|
||||||
|
|
||||||
# Test that any args with spaces remain quoted
|
# Test that any args with spaces remain quoted
|
||||||
assertEquals "$error" "$(printf -- '-m\tTest Commit Message\tone')" "$(scmb_expand_args -m "Test Commit Message" 1)"
|
assertEquals "$error" '-m Test\ Commit\ Message one' \
|
||||||
assertEquals "$error" "$(printf -- '-ma\tTest Commit Message\tUnquoted')"\
|
"$(eval args="$(scmb_expand_args -m "Test Commit Message" 1)"; token_quote "${args[@]}")"
|
||||||
"$(scmb_expand_args -ma "Test Commit Message" "Unquoted")"
|
assertEquals "$error" '-ma Test\ Commit\ Message Unquoted'\
|
||||||
|
"$(eval args="$(scmb_expand_args -ma "Test Commit Message" "Unquoted")"; token_quote "${args[@]}")"
|
||||||
|
assertEquals "$error" '\$dollar one two\ words' \
|
||||||
|
"$(eval args="$(scmb_expand_args 7 1-1 8)"; token_quote "${args[@]}")"
|
||||||
|
|
||||||
|
# Keep this code for use when minimum versions of {ba,z}sh can be increased.
|
||||||
|
# See token_quote() source and https://github.com/scmbreeze/scm_breeze/issues/260
|
||||||
|
# local e1="one" e2="two" e3="three" e4="four" e5="five" e6="six" e7='$dollar' e8='two words'
|
||||||
|
# local error="Args not expanded correctly"
|
||||||
|
# assertEquals "$error" "'one' 'three' 'six'" \
|
||||||
|
# "$(eval a=$(scmb_expand_args 1 3 6); token_quote "${a[@]}")"
|
||||||
|
# assertEquals "$error" "'one' 'two' 'three' 'five'" \
|
||||||
|
# "$(eval a=$(scmb_expand_args 1-3 5); token_quote "${a[@]}")"
|
||||||
|
# assertEquals "$error" "'\$dollar' 'two' 'three' 'four' 'one'" \
|
||||||
|
# "$(eval a=$(scmb_expand_args 7 2-4 1); token_quote "${a[@]}")"
|
||||||
|
#
|
||||||
|
# # Test that any args with spaces remain quoted
|
||||||
|
# assertEquals "$error" "'-m' 'Test Commit Message' 'one'" \
|
||||||
|
# "$(eval a=$(scmb_expand_args -m "Test Commit Message" 1); token_quote "${a[@]}")"
|
||||||
|
# assertEquals "$error" "'-ma' 'Test Commit Message' 'Unquoted'"\
|
||||||
|
# "$(eval a=$(scmb_expand_args -ma "Test Commit Message" "Unquoted"); token_quote "${a[@]}")"
|
||||||
|
# assertEquals "$error" "'\$dollar' 'one' 'two words'" \
|
||||||
|
# "$(eval a=$(scmb_expand_args 7 1-1 8); token_quote "${a[@]}")"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_exec_scmb_expand_args() {
|
||||||
|
local e1="one" e2="a b c" e3='$dollar' e4="single'quote" e5='double"quote' e6='a(){:;};a&'
|
||||||
|
assertEquals "literals with spaces not preserved" 'foo bar\ baz' \
|
||||||
|
"$(eval args="$(scmb_expand_args foo 'bar baz')"; token_quote "${args[@]}")"
|
||||||
|
assertEquals "variables with spaces not preserved" 'one a\ b\ c' \
|
||||||
|
"$(eval args="$(scmb_expand_args 1-2)"; token_quote "${args[@]}")"
|
||||||
|
# Expecting text: '$dollar' "single'quote" 'double"quote'
|
||||||
|
# Generate quoted expected string with: token_quote "$(cat)" then copy/paste, ^D
|
||||||
|
assertEquals "special characters are preserved" \
|
||||||
|
'\$dollar single\'\''quote double\"quote a\(\)\{:\;\}\;a\&' \
|
||||||
|
"$(eval args="$(scmb_expand_args 3-6)"; token_quote "${args[@]}")"
|
||||||
|
|
||||||
|
# Keep this code for use when minimum versions of {ba,z}sh can be increased.
|
||||||
|
# See token_quote() source and https://github.com/scmbreeze/scm_breeze/issues/260
|
||||||
|
# local e1="one" e2="a b c" e3='$dollar' e4="single'quote" e5='double"quote' e6='a(){:;};a&'
|
||||||
|
# assertEquals "literals with spaces not preserved" "'foo' 'bar baz'" \
|
||||||
|
# "$(eval a="$(scmb_expand_args foo 'bar baz')"; token_quote "${a[@]}")"
|
||||||
|
# assertEquals "variables with spaces not preserved" "'one' 'a b c'" \
|
||||||
|
# "$(eval a="$(scmb_expand_args 1-2)"; token_quote "${a[@]}")"
|
||||||
|
# # Expecting text: '$dollar' "single'quote" 'double"quote'
|
||||||
|
# # Generate quoted expected string with: token_quote "$(cat)" then copy/paste, ^D
|
||||||
|
# assertEquals "special characters are preserved" \
|
||||||
|
# "'\$dollar' 'single'\\''quote' 'double\"quote' 'a(){:;};a&'" \
|
||||||
|
# "$(eval a="$(scmb_expand_args 3-6)"; token_quote "${a[@]}")"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_command_wrapping_escapes_special_characters() {
|
test_command_wrapping_escapes_special_characters() {
|
||||||
assertEquals 'should escape | the pipe' "$(exec_scmb_expand_args echo "should escape | the pipe")"
|
assertEquals 'should escape | the pipe' "$(exec_scmb_expand_args echo should escape '|' the pipe)"
|
||||||
assertEquals 'should escape ; the semicolon' "$(exec_scmb_expand_args echo "should escape ; the semicolon")"
|
assertEquals 'should escape ; the semicolon' "$(exec_scmb_expand_args echo should escape ';' the semicolon)"
|
||||||
}
|
}
|
||||||
|
|
||||||
test_git_status_shortcuts() {
|
test_git_status_shortcuts() {
|
||||||
|
|||||||
26
test/lib/scm_breeze_test.sh
Executable file
26
test/lib/scm_breeze_test.sh
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export scmbDir="$( cd -P "$( dirname "$0" )" && pwd )/../.."
|
||||||
|
|
||||||
|
# Zsh compatibility
|
||||||
|
if [ -n "${ZSH_VERSION:-}" ]; then shell="zsh"; SHUNIT_PARENT=$0; setopt shwordsplit; fi
|
||||||
|
|
||||||
|
# Load test helpers
|
||||||
|
source "$scmbDir/test/support/test_helper.sh"
|
||||||
|
|
||||||
|
# Load functions to test
|
||||||
|
source "$scmbDir/lib/scm_breeze.sh"
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
# Unit tests
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
test__safe_eval() {
|
||||||
|
assertEquals "runs eval with simple words" 'one two three' "$(_safe_eval token_quote one two three)"
|
||||||
|
assertEquals "quotes spaces" 'a b\ c d' "$(_safe_eval token_quote a b\ c d)"
|
||||||
|
assertEquals "quotes special chars" 'a\ b \$dollar \\slash c\ d' "$(_safe_eval token_quote a\ b '$dollar' '\slash' c\ d)"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# load and run shUnit2
|
||||||
|
source "$scmbDir/test/support/shunit2"
|
||||||
@@ -25,9 +25,9 @@ SHUNIT_ERROR=2
|
|||||||
# enable strict mode by default
|
# enable strict mode by default
|
||||||
SHUNIT_STRICT=${SHUNIT_STRICT:-${SHUNIT_TRUE}}
|
SHUNIT_STRICT=${SHUNIT_STRICT:-${SHUNIT_TRUE}}
|
||||||
|
|
||||||
_shunit_warn() { echo "shunit2:WARN $@" >&2; }
|
_shunit_warn() { echo "shunit2:WARN $*" >&2; }
|
||||||
_shunit_error() { echo "shunit2:ERROR $@" >&2; }
|
_shunit_error() { echo "shunit2:ERROR $*" >&2; }
|
||||||
_shunit_fatal() { echo "shunit2:FATAL $@" >&2; exit ${SHUNIT_ERROR}; }
|
_shunit_fatal() { echo "shunit2:FATAL $*" >&2; exit ${SHUNIT_ERROR}; }
|
||||||
|
|
||||||
# specific shell checks
|
# specific shell checks
|
||||||
if [ -n "${ZSH_VERSION:-}" ]; then
|
if [ -n "${ZSH_VERSION:-}" ]; then
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ orig_cwd="$PWD"
|
|||||||
source "$scmbDir/lib/git/helpers.sh"
|
source "$scmbDir/lib/git/helpers.sh"
|
||||||
|
|
||||||
# Set up demo git user if not configured
|
# Set up demo git user if not configured
|
||||||
if [ -z "$(git config --global user.email)" ]; then
|
if [ -z "$(git config user.email)" ]; then
|
||||||
git config --global user.email "testuser@example.com"
|
git config user.email "testuser@example.com"
|
||||||
git config --global user.name "Test User"
|
git config user.name "Test User"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -26,24 +26,32 @@ tab_completions(){ echo "${COMPREPLY[@]}"; }
|
|||||||
silentGitCommands() {
|
silentGitCommands() {
|
||||||
git() { /usr/bin/env git "$@" > /dev/null 2>&1; }
|
git() { /usr/bin/env git "$@" > /dev/null 2>&1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
# Cancel silent git commands
|
# Cancel silent git commands
|
||||||
verboseGitCommands() {
|
verboseGitCommands() {
|
||||||
unset -f git
|
unset -f git
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Asserts
|
# Asserts
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Return 0 (shell's true) if "$1" contains string "$2"
|
||||||
_includes() {
|
_includes() {
|
||||||
if [ -n "$3" ]; then regex="$3"; else regex=''; fi
|
if [ -n "$3" ]; then regex="$3"; else regex=''; fi
|
||||||
if echo "$1" | grep -q$regex "$2"; then echo 0; else echo 1; fi
|
echo "$1" | grep -q"$regex" "$2" # exit status of quiet grep is returned
|
||||||
}
|
}
|
||||||
|
|
||||||
# assert $1 contains $2
|
# assert $1 contains $2
|
||||||
assertIncludes() {
|
assertIncludes() {
|
||||||
assertTrue "'$1' should have contained '$2'" $(_includes "$@")
|
_includes "$@"
|
||||||
|
local grep_exit=$?
|
||||||
|
assertTrue "'$1' should have contained '$2'" '[[ $grep_exit == 0 ]]'
|
||||||
}
|
}
|
||||||
|
|
||||||
# assert $1 does not contain $2
|
# assert $1 does not contain $2
|
||||||
assertNotIncludes() {
|
assertNotIncludes() {
|
||||||
assertFalse "'$1' should not have contained '$2'" $(_includes "$@")
|
_includes "$@"
|
||||||
|
local grep_return=$?
|
||||||
|
assertTrue "'$1' should not have contained '$2'" '[[ ! $grep_exit = 0 ]]'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user