# -*- mode: sh; sh-shell: bash -*-
# vim: set ft=bash:
# shellcheck shell=bash
# shellcheck disable=2016
### Git support library
# prototype: "branch" = "ext git_branch_complete"
# prototype: "git-ls-files-opts" = "extcomp git ls-files --cached --exclude-standard"
## Returns the path to the toplevel git repository.
function git_toplevel
{
memo git rev-parse --show-toplevel
}
## Returns the path to the bar that is initialized in the git toplevel.
## Fails when there is no bar initialized.
function git_bar
{
# shellcheck disable=SC2155
local tl="$(git_toplevel)"
if [[ -f "$tl/bar" ]]; then
echo "$tl/bar"
elif [[ -f "$tl/.bar" ]]; then
echo "$tl/.bar"
else
die "no bar file found in $tl"
fi
}
memofn git_bar
#git rev-parse --is-inside-work-tree
function git_dir ## Returns the absolute path to the 'GIT_DIR' (.git/).
{
## Note that this may be distinct from '$(git_toplevel)/.git/' when worktrees or other git
## features are used.
memo git rev-parse --absolute-git-dir
}
function git_ls_files ## [git-ls-files-opts].. - Sorted list of files in the git repository.
{
## git-ls-files '--exclude-standard' is always implied. Use '-c' or '--cached' to list
## tracked files, '--others' to show untracked files, or both for all files.
local zero_terminated=
local z
for z in "$@"; do
[[ "$z" = "-z" ]] && { zero_terminated=true; break; }
done
git ls-files --exclude-standard "$@" | sort -u ${zero_terminated:+-z}
}
function git_branch_name ## Returns the branch name that is checked out, dies when not in a git branch.
{
git branch --show-current
}
function git_branch_complete ## List all git branches for completion
{
## This function is called by bar_complete to provide completions for branch parameters.
git branch --list --format="%(refname:short)" 2>/dev/null
}
function git_branch_find ## [patterns].. - lists branches matching the given patterns
{
git branch --list --format="%(refname:short)" "$@"
}
function git_branch_find_one ## [patterns].. - lists the matching the given patterns, fails when there are more than one
{
local branches
mapfile branches < <(git_branch_find "$@")
[[ ${#branches[*]} != 1 ]] && return 1
echo "${branches[0]}"
}
function git_is_ancestor ## [patterns].. - checks if the current branch is a ancestor of the one branch matching the patterns
{
git merge-base --is-ancestor "$(git_branch_find_one "$@")" "$(git_branch_name)"
}
# TODO: rename git_dir_hash or similar
function git_tree_hash ## [git-ls-files-opts].. - Returns a sha1 hash over the current directory
{
## This hash is a identifier used by bar. It is not the same hash git using for storing trees.
local hash
# needs || true here to suppress errors on directories
hash="$(git_ls_files -z "$@" | { xargs -0 sha1sum 2>/dev/null || true ; } | sha1sum)"
echo "${hash:0:40}"
}
memofn git_tree_hash
function git_add_ignore ## [patterns..] - Adds new ignore patterns to '.gitignore'.
{
## Patterns that are already present are skipped.
[[ -f "$(git_toplevel)/.gitignore" ]] || touch "$(git_toplevel)/.gitignore"
local pattern
for pattern in "$@"; do
if ! grep -F "$pattern" "$(git_toplevel)/.gitignore" >/dev/null; then
[[ -n "$(tail -c 1 "$(git_toplevel)/.gitignore")" && "$(wc -c "$(git_toplevel)/.gitignore")" != 0 ]] &&
echo >>"$(git_toplevel)/.gitignore"
echo "$pattern" >>"$(git_toplevel)/.gitignore"
fi
done
}
function git_parse_worktrees ## [main [dirs [branches [heads]]]] - Parses git worktrees into variables provided by the caller
{
## - main: variable storing the main worktree (dir)
## - dirs: associative array storing 'branch:directory'
## - branches: associative array storing 'directory:branch'
## - heads: associative array storing 'directory:head'
iset "${1:-}" ""
iset "${2:-}"
iset "${3:-}"
iset "${4:-}"
local key value dir head
while read -r key value; do
case "$key" in
worktree)
dir="$value"
[[ -n "${1:-}" && -z "${!1}" ]] && iset "$1" "$value"
;;
HEAD)
head="$value"
;;
branch)
iset "${2:-}[${value##*/}]" "$dir"
iset "${3:-}[$dir]" "${value##*/}"
iset "${4:-}[$dir]" "$head"
;;
esac
done < <(git worktree list --porcelain)
}
# Define --bare rules for read-only git query functions
rule --bare git_toplevel: -
rule --bare git_bar: -
rule --bare git_dir: -
rule --bare git_ls_files: -
rule --bare git_branch_name: -
rule --bare git_branch_find: -
rule --bare git_branch_find_one: -
rule --bare git_is_ancestor: -
rule --bare git_tree_hash: -