mirror of
https://github.com/tj/git-extras.git
synced 2026-09-10 07:26:17 -04:00
286 lines
6.8 KiB
Bash
Executable file
286 lines
6.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
|
|
|
|
# awk script that takes 2 input files. The first one is newline deilmeted
|
|
# string of patterns to look for, second one is the file to remove them from.
|
|
# outfile has to be passed with -v upon call and is the file result is
|
|
# written to.
|
|
read -r -d '' AWK_SCRIPT <<'EOF'
|
|
# process the first "file" (newline split argument list)
|
|
NR==FNR { a[b++]=$0; next; }
|
|
# process file
|
|
{
|
|
found=0;
|
|
for (i=0; i != b; i++) {
|
|
if (a[i] == $0) { found=1; }
|
|
}
|
|
if (found == 1) { print "... removing '"$0"'"; }
|
|
else { print $0 > outfile }
|
|
}
|
|
EOF
|
|
|
|
|
|
|
|
show_contents() {
|
|
local file="${2/#~/$HOME}"
|
|
if [ -f "$file" ]; then
|
|
echo "$1 gitignore: $2" && cat "$file"
|
|
else
|
|
echo "There is no $1 .gitignore yet"
|
|
fi
|
|
}
|
|
|
|
cd_to_git_root() {
|
|
local error_level="$1"
|
|
|
|
if ! git rev-parse --git-dir &>/dev/null; then
|
|
if [ "$error_level" = '--warn' ]; then
|
|
echo "Warning: Not currently in a Git repository" >&2
|
|
elif [ "$error_level" = '--error' ]; then
|
|
echo "Error: Not currently in a Git repository" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
local result=
|
|
if result=$(git rev-parse --show-toplevel 2>/dev/null); then
|
|
cd "$result" || exit
|
|
fi
|
|
}
|
|
|
|
global_ignore() {
|
|
if ! git config --global core.excludesFile 2>/dev/null; then
|
|
if [ -f "$HOME/.gitignore" ]; then
|
|
echo "$HOME/.gitignore"
|
|
else
|
|
echo "${XDG_CONFIG_HOME:-$HOME/.config}/git/ignore"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
show_global() {
|
|
show_contents Global "$(global_ignore)"
|
|
}
|
|
|
|
add_global() {
|
|
local global_gitignore
|
|
global_gitignore="$(global_ignore)"
|
|
if [ -z "$global_gitignore" ]; then
|
|
echo "Can't find global .gitignore."
|
|
echo ""
|
|
echo "Use 'git config --global --add core.excludesfile ~/.gitignore-global' to set the path to your global gitignore file to '~/.gitignore-global'."
|
|
echo ""
|
|
else
|
|
add_patterns "$global_gitignore" "$@"
|
|
fi
|
|
}
|
|
|
|
show_local() {
|
|
cd_to_git_root --warn
|
|
show_contents Local .gitignore
|
|
}
|
|
|
|
add_local() {
|
|
cd_to_git_root --warn
|
|
add_patterns .gitignore "$@"
|
|
}
|
|
|
|
show_private() {
|
|
cd_to_git_root --error
|
|
show_contents Private "${GIT_DIR}/info/exclude"
|
|
}
|
|
|
|
add_private() {
|
|
cd_to_git_root --error
|
|
test -d "${GIT_DIR}/info" || mkdir -p "${GIT_DIR}/info"
|
|
add_patterns "${GIT_DIR}/info/exclude" "$@"
|
|
}
|
|
|
|
add_patterns() {
|
|
echo "Adding pattern(s) to: $1"
|
|
local file="${1/#~/$HOME}"
|
|
dir_name=$(dirname "$file")
|
|
if [ ! -d "$dir_name" ]; then
|
|
mkdir -p "$dir_name"
|
|
fi
|
|
if [ -s "$file" ]; then
|
|
# If the content of $file doesn't end with a newline, add one
|
|
test "$(tail -c 1 "$file")" != "" && echo "" >> "$file"
|
|
fi
|
|
for pattern in "${@:2}"; do
|
|
echo "... adding '$pattern'"
|
|
(test -f "$file" && test "$pattern" && grep -q -F -x -- "$pattern" "$file") || echo "$pattern" >> "$file"
|
|
done
|
|
}
|
|
|
|
edit_file() {
|
|
local file="${2/#~/$HOME}"
|
|
if [ -f "$file" ]; then
|
|
echo "Editing $1 gitignore: ($2)" && eval "$(git var GIT_EDITOR) $file"
|
|
echo "Done."
|
|
else
|
|
echo "There is no $1 .gitignore yet." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
_usage() {
|
|
cat << EOF
|
|
Usage: git ignore [OPTON]... [PATTERN]...
|
|
Modify or display local global or private gitignore files.
|
|
|
|
OPTIONs set context (local, global, private) and action (display, add,
|
|
remove, edit). In case of collision, last flag overwrites preceding ones.
|
|
Defaults are: local context, action is display (see man git ignore for
|
|
more details).
|
|
Context examples:
|
|
local: .../repo/.gitignore
|
|
private: .../repo/.git/info/exclude
|
|
global: \$HOME/.gitignore
|
|
|
|
PATTERNs are the gitignore patterns as specified in git documentation.
|
|
|
|
Options:
|
|
-l, --local Set context to local gitignore
|
|
-g, --global Set context to global gitignore
|
|
-p, --private Set context to private gitignore
|
|
-e, --edit Set action to edit
|
|
-r, --remove Set action to remove
|
|
-h, --help Print this message
|
|
-- End of options delimeter
|
|
EOF
|
|
}
|
|
|
|
remove_patterns() {
|
|
declare -a args
|
|
local tmpfile
|
|
args=( "${@:3}" )
|
|
local file="${2/#~/$HOME}"
|
|
if [ -f "$file" ]; then
|
|
tmpfile="$(mktemp -q "${TMPDIR:-/tmp}"/git-extras-ignore.XXXXXX 2>/dev/null)"
|
|
echo "Removing patterns from $1 gitignore: $2"
|
|
awk -v outfile="$tmpfile" "${AWK_SCRIPT}"\
|
|
<(printf '%s\n' "${args[@]}") "$file"
|
|
cat "$tmpfile" > "$file"
|
|
rm "$tmpfile"
|
|
else
|
|
echo "There is no $1 .gitignore yet"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
|
|
# can have only 1 action and 1 level, if conflicting flags are passed
|
|
# latter ones overwrite former
|
|
# sanity of args not tested (e.g. there should be no args if -e is supplied)
|
|
if test $# -eq 0; then
|
|
show_global
|
|
echo "---------------------------------"
|
|
show_local
|
|
echo "---------------------------------"
|
|
show_private
|
|
else
|
|
# parse flags and args
|
|
level="none"
|
|
action="none"
|
|
declare -a args
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-h|--help)
|
|
_usage
|
|
exit 0
|
|
;;
|
|
-l|--local)
|
|
level="local"
|
|
;;
|
|
-g|--global)
|
|
level="global"
|
|
;;
|
|
-p|--private)
|
|
level="private"
|
|
;;
|
|
-e|--edit)
|
|
action="edit"
|
|
;;
|
|
-r|--remove)
|
|
action="remove"
|
|
;;
|
|
--)
|
|
shift
|
|
args+=("$@")
|
|
break
|
|
;;
|
|
*)
|
|
args+=("$1")
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
# compatible with previous version
|
|
if [ "$action" = "none" ]; then
|
|
case "$level" in
|
|
local)
|
|
test ${#args[@]} -ne 0 && add_local "${args[@]}" && echo
|
|
show_local
|
|
;;
|
|
global)
|
|
test ${#args[@]} -ne 0 && add_global "${args[@]}" && echo
|
|
show_global
|
|
;;
|
|
private)
|
|
test ${#args[@]} -ne 0 && add_private "${args[@]}" && echo
|
|
show_private
|
|
;;
|
|
none)
|
|
add_local "${args[@]}"
|
|
# maybe show_local here too?
|
|
;;
|
|
esac
|
|
exit 0
|
|
# open file in editor
|
|
elif [ "$action" = "edit" ]; then
|
|
case "$level" in
|
|
local|none)
|
|
cd_to_git_root --warn
|
|
edit_file 'local' .gitignore && echo
|
|
;;
|
|
global)
|
|
global_gitignore="$(global_ignore)"
|
|
edit_file global "$global_gitignore" && echo
|
|
;;
|
|
private)
|
|
cd_to_git_root --error
|
|
test -d "${GIT_DIR}/info" || mkdir -p "${GIT_DIR}/info"
|
|
edit_file private "${GIT_DIR}/info/exclude" && echo
|
|
;;
|
|
esac
|
|
exit 0
|
|
# remove entries
|
|
else
|
|
if [ ${#args[@]} -eq 0 ]; then
|
|
echo "Nothing to remove."
|
|
exit 0
|
|
fi
|
|
case "$level" in
|
|
local|none)
|
|
cd_to_git_root --warn
|
|
remove_patterns 'local' .gitignore "${args[@]}" && echo
|
|
show_local
|
|
;;
|
|
global)
|
|
global_gitignore="$(global_ignore)"
|
|
remove_patterns global "$global_gitignore" "${args[@]}" && echo
|
|
show_global
|
|
;;
|
|
private)
|
|
cd_to_git_root --error
|
|
remove_patterns private "${GIT_DIR}/info/exclude" "${args[@]}" && echo
|
|
show_private
|
|
;;
|
|
esac
|
|
exit 0
|
|
|
|
fi
|
|
fi
|