Add configuration for git
- Add .config/git/config + Define aliases + Configure default behavior in various situations + Add user information with signing key - Add .config/git/commit_msg_template.txt - Add .config/git/ignore - Integrate `git` aliases into the shell
This commit is contained in:
parent
3017a7bdec
commit
721c2fd905
21 changed files with 1122 additions and 66 deletions
35
.local/bin/git-aliases
Executable file
35
.local/bin/git-aliases
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/sh
|
||||
|
||||
# List the names of all `git` aliases, one per line
|
||||
#
|
||||
# With the "--real" flag, only the "real" aliases are listed,
|
||||
# which are those with less than 7 characters in their name
|
||||
# as defined under "[alias]" in ~/.config/git/config
|
||||
#
|
||||
# Usage: git aliases [--real]
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
_max_length=999
|
||||
|
||||
|
||||
for arg in "$@"; do
|
||||
|
||||
case "$arg" in
|
||||
--real)
|
||||
_max_length=7
|
||||
;;
|
||||
*)
|
||||
echo 'Usage: git aliases [--real]' >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
done
|
||||
|
||||
|
||||
|
||||
git --list-cmds=alias \
|
||||
| awk -v max="$_max_length" 'length($0) < max' \
|
||||
| sort
|
||||
15
.local/bin/git-aliases-n-sub-commands
Executable file
15
.local/bin/git-aliases-n-sub-commands
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
|
||||
# List the names of all `git` aliases and custom sub-commands, one per line
|
||||
#
|
||||
# Usage: git aliases-n-sub-commands
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
{
|
||||
|
||||
git --list-cmds=alias
|
||||
git --list-cmds=others
|
||||
|
||||
} | sort -u
|
||||
93
.local/bin/git-cat-all
Executable file
93
.local/bin/git-cat-all
Executable file
|
|
@ -0,0 +1,93 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Print the contents of all tracked text files to stdout,
|
||||
# each preceded by a header with its filename
|
||||
#
|
||||
# With "-a", binary files are included as well
|
||||
#
|
||||
# With "-m", only files with unstaged modifications are printed
|
||||
#
|
||||
# With "-n", the headers are omitted
|
||||
#
|
||||
# Usage: git cat-all [-a] [-m] [-n] [<pathspec> ...]
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
|
||||
# Error if we are not in a `git` repository
|
||||
git rev-parse --git-dir >/dev/null
|
||||
|
||||
# Without any commit, there is nothing to print reliably
|
||||
git rev-parse --verify --quiet HEAD >/dev/null || {
|
||||
echo "fatal: no commits yet" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
_textonly=yes
|
||||
_lsflags=
|
||||
_cmd='printf "\n\n===== %s =====\n\n" "{}"; cat "{}"'
|
||||
_headers=yes
|
||||
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case $1 in
|
||||
-a) _textonly=no
|
||||
shift
|
||||
;;
|
||||
-m) _lsflags=-m
|
||||
shift
|
||||
;;
|
||||
-n) _cmd='printf "\n\n"; cat "{}"'
|
||||
_headers=no
|
||||
shift
|
||||
;;
|
||||
--) shift
|
||||
break
|
||||
;;
|
||||
-*) echo "Usage: git cat-all [-a] [-m] [-n] [<pathspec> ...]" >&2
|
||||
exit 2
|
||||
;;
|
||||
*) break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
|
||||
# Because `git` runs scripts on the `$PATH` in the current directory
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
|
||||
|
||||
# An empty file counts as text
|
||||
# => `grep` alone would misjudge it
|
||||
# because it never matches in a file without any lines
|
||||
if [ "$_textonly" = "yes" ]; then
|
||||
_cmd='if [ ! -s "{}" ] || grep -Iq "" "{}"; then '"$_cmd"'; fi'
|
||||
fi
|
||||
|
||||
|
||||
# `git ls-files -m` also lists files deleted from the working tree
|
||||
# => Skip them because `cat` would fail
|
||||
if [ -n "$_lsflags" ]; then
|
||||
_cmd='if [ -f "{}" ]; then '"$_cmd"'; fi'
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if [ "$_headers" = "yes" ]; then
|
||||
printf "Repository: %s\n" "$(basename "$PWD")"
|
||||
printf "Branch: %s @ %s\n" \
|
||||
"$(git rev-parse --abbrev-ref HEAD)" \
|
||||
"$(git rev-parse --short HEAD)"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
git ls-files \
|
||||
-z $_lsflags \
|
||||
-- \
|
||||
"$@" \
|
||||
| xargs -0 -I{} sh -c "$_cmd"
|
||||
8
.local/bin/git-checkout-dev
Executable file
8
.local/bin/git-checkout-dev
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Check out the "develop" branch, whatever it is called
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
exec git checkout-first-existing-branch dev develop development
|
||||
28
.local/bin/git-checkout-first-existing-branch
Executable file
28
.local/bin/git-checkout-first-existing-branch
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Check out the first of the given branches that exists,
|
||||
# looking at both local branches and the ones on `origin`
|
||||
#
|
||||
# Usage: git checkout-first-existing-branch <branch> [<branch> ...]
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: git checkout-first-existing-branch <branch> [<branch> ...]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
|
||||
for _branch in "$@"; do
|
||||
|
||||
if git show-ref --verify --quiet "refs/heads/$_branch" \
|
||||
|| git show-ref --verify --quiet "refs/remotes/origin/$_branch"; then
|
||||
exec git checkout "$_branch"
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
|
||||
echo "None of these branches exists: $*" >&2
|
||||
exit 1
|
||||
8
.local/bin/git-checkout-main
Executable file
8
.local/bin/git-checkout-main
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Check out the "main" branch, whatever it is called
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
exec git checkout-first-existing-branch main master
|
||||
54
.local/bin/git-clean-all
Executable file
54
.local/bin/git-clean-all
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Reset the working directory to `HEAD` and remove everything not tracked,
|
||||
# except the files listed in $KEEP in ~/.local/bin/git-clean-ignored
|
||||
#
|
||||
# This is irreversible, so the changes are shown first and confirmed,
|
||||
# unless "--force" is passed
|
||||
#
|
||||
# Usage: git clean-all [--force]
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
# `git` runs scripts on the `$PATH` in the current directory,
|
||||
# whereas it runs "!" aliases at the top level of the repository
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
|
||||
|
||||
case "${1:-}" in
|
||||
--force) _forced=1 ;;
|
||||
"") _forced=0 ;;
|
||||
*) echo "Usage: git clean-all [--force]" >&2; exit 2 ;;
|
||||
esac
|
||||
|
||||
|
||||
if [ "$_forced" -ne 1 ]; then
|
||||
|
||||
echo "Tracked files to be reset to HEAD:"
|
||||
git diff --stat HEAD || true
|
||||
echo ""
|
||||
|
||||
echo "Untracked and ignored files to be removed:"
|
||||
# Keep the "-e" flags in sync with the other two copies:
|
||||
# - the "-e" flags of `clean-untracked` in ~/.config/git/config
|
||||
# - `$KEEP` in ~/.local/bin/git-clean-ignored
|
||||
git clean -x -d -n -e ".python-version" -e ".env"
|
||||
echo ""
|
||||
|
||||
printf "This CANNOT be undone. Continue? [y/N] "
|
||||
read -r _answer || _answer=""
|
||||
|
||||
case "$_answer" in
|
||||
[yY] | [yY][eE][sS]) ;;
|
||||
*) echo "Aborted" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
fi
|
||||
|
||||
|
||||
git reset --hard
|
||||
|
||||
git clean-ignored
|
||||
|
||||
git clean-untracked
|
||||
46
.local/bin/git-clean-ignored
Executable file
46
.local/bin/git-clean-ignored
Executable file
|
|
@ -0,0 +1,46 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Remove all files ignored by `git`, except the ones listed in $KEEP below
|
||||
#
|
||||
# `git clean` does not accept "-e" together with "-X",
|
||||
# so the files to be kept are moved aside and then restored
|
||||
#
|
||||
# Usage: git clean-ignored [<git clean options>]
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
# Keep this list in sync with the other two copies:
|
||||
# - the "-e" flags of `clean-untracked` in ~/.config/git/config
|
||||
# - the preview in ~/.local/bin/git-clean-all
|
||||
KEEP=".env .python-version"
|
||||
|
||||
SUFFIX=".git-clean-ignored.bak"
|
||||
|
||||
|
||||
# `git` runs scripts on the `$PATH` in the current directory,
|
||||
# whereas it runs "!" aliases at the top level of the repository
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
|
||||
|
||||
_restore() {
|
||||
for _file in $KEEP; do
|
||||
if [ -f "$_file$SUFFIX" ]; then
|
||||
mv "$_file$SUFFIX" "$_file"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# Restore the kept files even if `git clean` fails or the user aborts
|
||||
trap _restore EXIT HUP INT TERM
|
||||
|
||||
|
||||
for _file in $KEEP; do
|
||||
if [ -f "$_file" ]; then
|
||||
mv "$_file" "$_file$SUFFIX"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
git clean -X -d -f "$@"
|
||||
32
.local/bin/git-ff-all
Executable file
32
.local/bin/git-ff-all
Executable file
|
|
@ -0,0 +1,32 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Fast-forward all local branches that track a remote branch
|
||||
#
|
||||
# Branches with local-only commits are skipped
|
||||
#
|
||||
# Usage: git ff-all
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
git fetch --all --prune
|
||||
|
||||
|
||||
current="$(git branch --show-current)"
|
||||
|
||||
|
||||
git for-each-ref --format='%(refname:short) %(upstream:short)' refs/heads/ |
|
||||
while read -r branch upstream; do
|
||||
|
||||
[ -n "$upstream" ] || continue
|
||||
|
||||
if [ "$branch" = "$current" ]; then
|
||||
git merge --ff-only "$upstream" 2>/dev/null \
|
||||
|| echo "Skipped $branch (not a fast forward)"
|
||||
continue
|
||||
fi
|
||||
|
||||
git fetch . "$upstream:$branch" 2>/dev/null \
|
||||
|| echo "Skipped $branch (not a fast forward)"
|
||||
|
||||
done
|
||||
18
.local/bin/git-gc-everything
Executable file
18
.local/bin/git-gc-everything
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Delete EVERYTHING not reachable from a branch from the repository
|
||||
#
|
||||
# This expires the reflog as well!
|
||||
#
|
||||
# Usage: git gc-everything [<git gc options>]
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
exec git \
|
||||
-c gc.pruneExpire=now \
|
||||
-c gc.reflogExpire=0 \
|
||||
-c gc.reflogExpireUnreachable=0 \
|
||||
-c gc.rerereresolved=0 \
|
||||
-c gc.rerereunresolved=0 \
|
||||
gc "$@"
|
||||
35
.local/bin/git-match-remote
Executable file
35
.local/bin/git-match-remote
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Make all local branches match the remote branch they track,
|
||||
# discarding local-only commits and, on the current branch,
|
||||
# uncommitted changes as well
|
||||
#
|
||||
# Branches without an upstream are left alone
|
||||
#
|
||||
# Usage: git match-remote
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
git fetch --all --prune
|
||||
|
||||
|
||||
current="$(git branch --show-current)"
|
||||
|
||||
|
||||
git for-each-ref --format='%(refname:short) %(upstream:short)' refs/heads/ |
|
||||
while read -r branch upstream; do
|
||||
|
||||
[ -n "$upstream" ] || continue
|
||||
|
||||
git rev-parse --verify --quiet "$upstream" >/dev/null || continue
|
||||
|
||||
if [ "$branch" = "$current" ]; then
|
||||
git reset --hard "$upstream"
|
||||
continue
|
||||
fi
|
||||
|
||||
git branch -f "$branch" "$upstream"
|
||||
echo "Moved $branch to $upstream"
|
||||
|
||||
done
|
||||
21
.local/bin/git-point-branch-at
Executable file
21
.local/bin/git-point-branch-at
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Move a branch to a commit, whether or not it is checked out
|
||||
#
|
||||
# Usage: git point-branch-at [<branch>] <commit>
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
case $# in
|
||||
1) _branch=$(git rev-parse --abbrev-ref HEAD); _commit=$1 ;;
|
||||
2) _branch=$1; _commit=$2 ;;
|
||||
*) echo "Usage: git point-branch-at [<branch>] <commit>" >&2; exit 2 ;;
|
||||
esac
|
||||
|
||||
|
||||
if [ "$_branch" = "$(git rev-parse --abbrev-ref HEAD)" ]; then
|
||||
exec git reset --keep "$_commit"
|
||||
else
|
||||
exec git branch --force "$_branch" "$_commit"
|
||||
fi
|
||||
23
.local/bin/git-prune-delete
Executable file
23
.local/bin/git-prune-delete
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Delete the local branches whose upstream branch was deleted on the remote
|
||||
#
|
||||
# `git branch --delete` without "--force" is used,
|
||||
# so branches holding unmerged commits are kept
|
||||
#
|
||||
# Usage: git prune-delete
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
_branches=$(git prune-show)
|
||||
|
||||
|
||||
if [ -z "$_branches" ]; then
|
||||
echo "No branches to prune"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
# shellcheck disable=SC2086 # because branch names never contain whitespace
|
||||
git branch --delete $_branches
|
||||
15
.local/bin/git-prune-show
Executable file
15
.local/bin/git-prune-show
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Show the local branches whose upstream branch was deleted on the remote
|
||||
#
|
||||
# Usage: git prune-show
|
||||
|
||||
set -eu
|
||||
|
||||
|
||||
git fetch --prune
|
||||
|
||||
git branch \
|
||||
--format='%(refname:short) %(upstream:track)' \
|
||||
--list \
|
||||
| awk '$2 == "[gone]" { print $1 }'
|
||||
Loading…
Reference in a new issue