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
63
.bashrc
63
.bashrc
|
|
@ -69,7 +69,6 @@ PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
|
|||
|
||||
|
||||
# Enable tab completion
|
||||
|
||||
if ! shopt -oq posix; then
|
||||
if [ -f /usr/share/bash-completion/bash_completion ]; then
|
||||
. /usr/share/bash-completion/bash_completion
|
||||
|
|
@ -78,68 +77,6 @@ if ! shopt -oq posix; then
|
|||
fi
|
||||
fi
|
||||
|
||||
# Add tab completion for all aliases to commands with completion functions
|
||||
# (must come after bash completions have been set up)
|
||||
# Source: https://superuser.com/a/437508
|
||||
_alias_completion() {
|
||||
local namespace="alias_completion"
|
||||
# parse function based completion definitions, where capture group 2 => function and 3 => trigger
|
||||
local compl_regex='complete( +[^ ]+)* -F ([^ ]+) ("[^"]+"|[^ ]+)'
|
||||
# parse alias definitions, where capture group 1 => trigger, 2 => command, 3 => command arguments
|
||||
local alias_regex="alias ([^=]+)='(\"[^\"]+\"|[^ ]+)(( +[^ ]+)*)'"
|
||||
# create array of function completion triggers, keeping multi-word triggers together
|
||||
eval "local completions=($(complete -p | sed -Ene "/$compl_regex/s//'\3'/p"))"
|
||||
(( ${#completions[@]} == 0 )) && return 0
|
||||
# create temporary file for wrapper functions and completions
|
||||
rm -f "/tmp/${namespace}-*.tmp" # preliminary cleanup
|
||||
local tmp_file; tmp_file="$(mktemp "/tmp/${namespace}-${RANDOM}XXX.tmp")" || return 1
|
||||
local completion_loader; completion_loader="$(complete -p -D 2>/dev/null | sed -Ene 's/.* -F ([^ ]*).*/\1/p')"
|
||||
# read in "<alias> '<aliased command>' '<command args>'" lines from defined aliases
|
||||
local line; while read line; do
|
||||
eval "local alias_tokens; alias_tokens=($line)" 2>/dev/null || continue # some alias arg patterns cause an eval parse error
|
||||
local alias_name="${alias_tokens[0]}" alias_cmd="${alias_tokens[1]}" alias_args="${alias_tokens[2]# }"
|
||||
# skip aliases to pipes, boolean control structures and other command lists
|
||||
# (leveraging that eval errs out if $alias_args contains unquoted shell metacharacters)
|
||||
eval "local alias_arg_words; alias_arg_words=($alias_args)" 2>/dev/null || continue
|
||||
# avoid expanding wildcards
|
||||
read -a alias_arg_words <<< "$alias_args"
|
||||
# skip alias if there is no completion function triggered by the aliased command
|
||||
if [[ ! " ${completions[*]} " =~ " $alias_cmd " ]]; then
|
||||
if [[ -n "$completion_loader" ]]; then
|
||||
# force loading of completions for the aliased command
|
||||
eval "$completion_loader $alias_cmd"
|
||||
# 124 means completion loader was successful
|
||||
[[ $? -eq 124 ]] || continue
|
||||
completions+=($alias_cmd)
|
||||
else
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
local new_completion="$(complete -p "$alias_cmd")"
|
||||
# create a wrapper inserting the alias arguments if any
|
||||
if [[ -n $alias_args ]]; then
|
||||
local compl_func="${new_completion/#* -F /}"; compl_func="${compl_func%% *}"
|
||||
# avoid recursive call loops by ignoring our own functions
|
||||
if [[ "${compl_func#_$namespace::}" == $compl_func ]]; then
|
||||
local compl_wrapper="_${namespace}::${alias_name}"
|
||||
echo "function $compl_wrapper {
|
||||
(( COMP_CWORD += ${#alias_arg_words[@]} ))
|
||||
COMP_WORDS=($alias_cmd $alias_args \${COMP_WORDS[@]:1})
|
||||
(( COMP_POINT -= \${#COMP_LINE} ))
|
||||
COMP_LINE=\${COMP_LINE/$alias_name/$alias_cmd $alias_args}
|
||||
(( COMP_POINT += \${#COMP_LINE} ))
|
||||
$compl_func
|
||||
}" >> "$tmp_file"
|
||||
new_completion="${new_completion/ -F $compl_func / -F $compl_wrapper }"
|
||||
fi
|
||||
fi
|
||||
# replace completion trigger by alias
|
||||
new_completion="${new_completion% *} $alias_name"
|
||||
echo "$new_completion" >> "$tmp_file"
|
||||
done < <(alias -p | sed -Ene "s/$alias_regex/\1 '\2' '\3'/p")
|
||||
. "$tmp_file" && \rm -f "$tmp_file"
|
||||
}; _alias_completion
|
||||
|
||||
|
||||
# Jump to frequently used directories with `z <fragment>`
|
||||
if _command_exists zoxide; then
|
||||
|
|
|
|||
10
.config/git/README.md
Normal file
10
.config/git/README.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# `git`-related Configurations
|
||||
|
||||
This folder contains
|
||||
the [config](./config),
|
||||
[global ignores](./ignore), and
|
||||
the [commit message template](./commit_msg_template.txt)
|
||||
for [`git`](https://git-scm.com/).
|
||||
|
||||
Machine-local settings (e.g., signing keys)
|
||||
go into an untracked `config_local`.
|
||||
17
.config/git/commit_msg_template.txt
Normal file
17
.config/git/commit_msg_template.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# SUBJECT -> What does the commit do?
|
||||
# - Imperative mood, no "." at the end
|
||||
# - Start with "Add", "Fix", "Make", "Remove", ...
|
||||
#---------- 50 characters / 1 line ---------------|
|
||||
|
||||
|
||||
# BODY -> The why and how
|
||||
# - Use plain text without formatting
|
||||
# + Use "-" and "+" as bullets
|
||||
# - "See: URL" to link to external resources
|
||||
#---------- 72 characters / multiple lines -----------------------------|
|
||||
|
||||
|
||||
# ISSUE TRACKER -> Uncomment one of the lines below
|
||||
# Closes #41
|
||||
# Fixes #42
|
||||
# Resolves #43
|
||||
626
.config/git/config
Normal file
626
.config/git/config
Normal file
|
|
@ -0,0 +1,626 @@
|
|||
# This config requires `git` v2.40+
|
||||
# (e.g., Debian 12's v2.39 is too old)
|
||||
#
|
||||
# The most recent features used are:
|
||||
#
|
||||
# v2.40 help.autocorrect with "prompt"
|
||||
# v2.38 rebase.updateRefs
|
||||
# v2.37 push.autoSetupRemote
|
||||
# v2.35 merge.conflictstyle with "zdiff3"
|
||||
#
|
||||
# Older `git` versions do NOT fail on all of these
|
||||
# but may warn about some unknown values
|
||||
|
||||
|
||||
|
||||
[alias]
|
||||
|
||||
# `git` commands are defined in three places:
|
||||
#
|
||||
# 1) Plain abbreviations and short "!" one-liners
|
||||
# are defined in this file
|
||||
#
|
||||
# 2) ~/.local/bin/ holds `git-*` utilities
|
||||
# for everything with control flow or an error path,
|
||||
# which `git` registers as sub-commands automatically
|
||||
#
|
||||
# 3) ~/.config/shell/aliases defines short form aliases for the shell,
|
||||
# for example, `gap` for `git add --patch`
|
||||
#
|
||||
# Below, the aliases are grouped by task:
|
||||
# Full-name aliases come first in each group,
|
||||
# followed by short abbreviations
|
||||
#
|
||||
# Tab complete works out of the box in `bash`,
|
||||
# whereas a `zstyle` line in ~/.zshrc makes that work for `zsh`
|
||||
#
|
||||
# Run:
|
||||
# - `git aliases` for just 1)
|
||||
# - `git aliases-n-sub-commands` for 1) and 2)
|
||||
# - `git help -a` for ALL sub-commands, built-in and custom
|
||||
|
||||
|
||||
# Inspect the repository
|
||||
#
|
||||
# See also this sub-command in ~/.local/bin:
|
||||
#
|
||||
# git-cat-all
|
||||
|
||||
current-branch = rev-parse --abbrev-ref HEAD
|
||||
project-root = rev-parse --show-toplevel
|
||||
|
||||
# List all authors with their commit counts
|
||||
contributors = shortlog --summary --numbered --email
|
||||
|
||||
# Ignore whitespace and follow lines moved or copied between files
|
||||
blame-follow = blame -w -C -C -C
|
||||
|
||||
bi = bisect
|
||||
bl = blame-follow
|
||||
rl = reflog
|
||||
sh = show
|
||||
|
||||
s = status
|
||||
ss = status --short
|
||||
|
||||
|
||||
# Stage changes & sync the working directory into the index
|
||||
|
||||
sync = !git sync-deleted && git add . --all
|
||||
sync-deleted = !git ls-files -z --deleted | xargs -r -0 git rm
|
||||
|
||||
a = add
|
||||
aa = add --all
|
||||
ap = add --patch
|
||||
|
||||
# No-ops so that the `gmv` and `grm` aliases are created
|
||||
mv = mv
|
||||
rm = rm
|
||||
|
||||
|
||||
# Commit changes
|
||||
|
||||
# Keep the commit message and skip the editor
|
||||
amend-commit = commit --amend --no-edit
|
||||
|
||||
ci = commit
|
||||
cia = commit --amend
|
||||
cian = amend-commit
|
||||
cif = commit --fixup
|
||||
cim = commit --message
|
||||
|
||||
|
||||
# Undo things, named as "specified" in the `git status` header
|
||||
|
||||
discard = checkout --
|
||||
uncommit = reset --soft HEAD~1
|
||||
unstage = reset HEAD --
|
||||
|
||||
rev = revert
|
||||
|
||||
rh = reset
|
||||
rhh = reset --hard
|
||||
rhs = reset --soft
|
||||
|
||||
rs = restore
|
||||
rst = restore --staged
|
||||
|
||||
u = uncommit
|
||||
|
||||
|
||||
# Manage tags
|
||||
|
||||
de = describe --tags
|
||||
|
||||
ta = tag
|
||||
taa = tag --annotate
|
||||
tad = tag --delete
|
||||
tal = tag --list
|
||||
|
||||
|
||||
# Make minimal diff the default
|
||||
|
||||
diff-minimal = diff --color-words=. --ws-error-highlight=all
|
||||
diff-last-commit = diff --color-words=. --ws-error-highlight=all HEAD
|
||||
diff-staged = diff --color-words=. --ws-error-highlight=all --staged
|
||||
|
||||
d = diff-minimal
|
||||
dlc = diff-last-commit
|
||||
ds = diff-staged
|
||||
|
||||
|
||||
# Make the logs look nice by default
|
||||
|
||||
log-format1 = log --pretty='%C(auto)%h: %s%d%Creset%n%C(dim)%an @ %ad => %ar%n' --date=format:'%a %Y-%m-%d %H:%M:%S %z'
|
||||
log-format2 = log --pretty='%C(auto)%h: %s%d%Creset%n%C(dim)%aN @ %ad => %ar%n%+b' --date=format:'%a %Y-%m-%d %H:%M:%S %z'
|
||||
|
||||
history = log-format2 --graph
|
||||
summary = log-format1 --graph
|
||||
oneline = log --pretty='%C(auto)%h: %s%d' --graph
|
||||
|
||||
last-commit = log-format2 -1 -p --stat
|
||||
|
||||
lg = log
|
||||
lga = log --all
|
||||
|
||||
hi = history
|
||||
hia = history --all
|
||||
|
||||
su = summary
|
||||
sua = summary --all
|
||||
|
||||
ol = oneline
|
||||
ola = oneline --all
|
||||
|
||||
lc = last-commit
|
||||
|
||||
|
||||
# Search the history of specific files/paths
|
||||
|
||||
# Follow a single file through renames
|
||||
history-file = log-format2 --graph --follow --
|
||||
history-file-diff = log-format2 --graph --follow -p --stat --
|
||||
|
||||
# Show all commits touching a path (e.g., a folder)
|
||||
history-path = log-format2 --graph --
|
||||
history-path-diff = log-format2 --graph -p --stat --
|
||||
|
||||
hif = history-file
|
||||
hifd = history-file-diff
|
||||
|
||||
hip = history-path
|
||||
hipd = history-path-diff
|
||||
|
||||
|
||||
# Search the repository
|
||||
|
||||
# Search the tracked files for source code,
|
||||
# showing the enclosing function of each match
|
||||
grep-code = grep --break --context 1 --full-name --heading --line-number --show-function
|
||||
|
||||
# Search the commit messages across all branches
|
||||
grep-commit-msg = log --all --regexp-ignore-case --pretty='%C(auto)%h: %s%+D%Creset%n%C(reverse)%C(dim)%aN @ %ad = %ar%Creset%n%+b' --date=format:'%a %Y-%m-%d %H:%M:%S' --grep
|
||||
|
||||
# Find commits whose diff contains added or removed lines
|
||||
# matching a regex, with or without the patches shown
|
||||
grep-diff = log-format1 -p -G
|
||||
grep-diff-list = log-format1 -G
|
||||
|
||||
# Find commits that change the NUMBER of occurrences of a regex,
|
||||
# which ignores lines merely moved around (unlike grep-diff)
|
||||
grep-pickaxe = log-format1 -p --pickaxe-regex -S
|
||||
|
||||
# Search the tracked files for plain text, ignoring case
|
||||
grep-text = grep --break --context 1 --full-name --heading --ignore-case --line-number
|
||||
|
||||
repc = grep-code
|
||||
repcim = grep-commit-msg
|
||||
repd = grep-diff
|
||||
repdl = grep-diff-list
|
||||
repp = grep-pickaxe
|
||||
rept = grep-text
|
||||
|
||||
|
||||
# Manage the branches & move between them
|
||||
#
|
||||
# See also these sub-commands in ~/.local/bin:
|
||||
#
|
||||
# git-checkout-dev
|
||||
# git-checkout-main
|
||||
# git-checkout-first-existing-branch
|
||||
#
|
||||
# git-point-branch-at
|
||||
#
|
||||
# git-prune-delete
|
||||
# git-prune-show
|
||||
|
||||
# `git branch` sorts by recency (see [branch] below)
|
||||
# => This alias restores the alphabetical listing
|
||||
branch-alpha = branch --sort=refname
|
||||
|
||||
rename-branch = branch --move
|
||||
|
||||
br = branch
|
||||
bra = branch --all
|
||||
|
||||
brd = branch --delete
|
||||
brdd = branch --delete --force
|
||||
|
||||
brm = rename-branch
|
||||
brp = point-branch-at
|
||||
|
||||
# Which branches are (not) fully contained in `HEAD`
|
||||
brme = branch --all --merged
|
||||
brnome = branch --all --no-merged
|
||||
|
||||
co = checkout
|
||||
cob = checkout -b
|
||||
cod = checkout-dev
|
||||
com = checkout-main
|
||||
|
||||
sw = switch
|
||||
swc = switch --create
|
||||
|
||||
|
||||
# Integrate changes from other branches
|
||||
|
||||
cp = cherry-pick
|
||||
|
||||
mb = merge-base
|
||||
|
||||
me = merge
|
||||
mea = merge --abort
|
||||
mec = merge --continue
|
||||
meff = merge --ff-only
|
||||
menoff = merge --no-ff
|
||||
|
||||
rb = rebase --committer-date-is-author-date
|
||||
rba = rebase --abort
|
||||
rbc = rebase --continue
|
||||
rbi = rebase --interactive # --autosquash is added in [rebase] below
|
||||
rbia = rebase --interactive --autosquash
|
||||
rbq = rebase --quit
|
||||
rbs = rebase --skip
|
||||
|
||||
|
||||
# Exchange changes with the remotes
|
||||
#
|
||||
# See also these sub-commands in ~/.local/bin:
|
||||
#
|
||||
# git-ff-all
|
||||
# git-match-remote
|
||||
|
||||
cl = clone
|
||||
|
||||
fe = fetch # --prune is added in
|
||||
fea = fetch --all # [fetch] below
|
||||
fep = fetch --prune
|
||||
fepa = fetch --prune --all
|
||||
|
||||
ff = ff-all
|
||||
fff = match-remote # ~ fast-forward with force
|
||||
|
||||
pl = pull
|
||||
plrb = pull --rebase
|
||||
|
||||
ps = push
|
||||
psf = push --force-with-lease
|
||||
psff = push --force
|
||||
|
||||
re = remote --verbose
|
||||
|
||||
|
||||
# Put changes aside temporarily
|
||||
|
||||
st = stash
|
||||
sta = stash push --include-untracked
|
||||
stam = stash push --include-untracked --message
|
||||
stapp = stash apply
|
||||
stl = stash list
|
||||
stp = stash pop
|
||||
stsh = stash show
|
||||
|
||||
|
||||
# Work with several working directories
|
||||
|
||||
wt = worktree
|
||||
wta = worktree add
|
||||
wtl = worktree list
|
||||
wtm = worktree move
|
||||
wtp = worktree prune
|
||||
wtr = worktree remove
|
||||
|
||||
|
||||
# Clean the project folder with intuitive commands
|
||||
#
|
||||
# See also these sub-commands in ~/.local/bin:
|
||||
#
|
||||
# git-clean-all
|
||||
# git-clean-ignored
|
||||
#
|
||||
# git-gc-everything
|
||||
|
||||
# Keep this "-e" list in sync with the other two copies:
|
||||
# - `$KEEP` in ~/.local/bin/git-clean-ignored
|
||||
# - the preview in ~/.local/bin/git-clean-all
|
||||
clean-untracked = clean -x -d -e ".python-version" -e ".env" -f # because the "-e" flag does not work with "-X"
|
||||
|
||||
cln = clean --interactive
|
||||
|
||||
|
||||
|
||||
[branch]
|
||||
|
||||
# `git branch` lists the most recently used branches first
|
||||
sort = -committerdate
|
||||
|
||||
|
||||
|
||||
[clean]
|
||||
|
||||
# Refuse to run `git clean` without --force or --dry-run
|
||||
requireforce = true
|
||||
|
||||
|
||||
|
||||
[column]
|
||||
|
||||
# Print listings (e.g., branches) in columns
|
||||
ui = auto
|
||||
|
||||
|
||||
|
||||
[color "branch"]
|
||||
|
||||
current = cyan dim bold reverse
|
||||
local = green bold
|
||||
remote = cyan bold
|
||||
|
||||
|
||||
[color "decorate"]
|
||||
|
||||
HEAD = cyan dim bold reverse
|
||||
branch = green bold
|
||||
remoteBranch = cyan bold
|
||||
stash = magenta dim bold reverse
|
||||
tag = magenta bold
|
||||
|
||||
|
||||
[color "diff"]
|
||||
|
||||
context = white
|
||||
frag = blue dim bold reverse
|
||||
meta = yellow dim bold reverse
|
||||
new = green bold
|
||||
old = red bold
|
||||
whitespace = red dim bold reverse
|
||||
|
||||
# Lines that are merely moved around (see "diff.colorMoved" below)
|
||||
newMoved = cyan bold
|
||||
oldMoved = magenta bold
|
||||
|
||||
# Alternation shades to tell adjacent moved blocks apart (~ "zebra")
|
||||
newMovedAlternative = cyan
|
||||
oldMovedAlternative = magenta
|
||||
|
||||
|
||||
[color "grep"]
|
||||
|
||||
context = white
|
||||
filename = yellow dim bold reverse
|
||||
function = white bold
|
||||
linenumber = blue dim bold reverse
|
||||
match = yellow bold
|
||||
selected = white
|
||||
separator = blue dim bold reverse
|
||||
|
||||
|
||||
[color "interactive"]
|
||||
|
||||
error = red dim bold reverse
|
||||
header = white
|
||||
help = blue bold
|
||||
prompt = white dim bold reverse
|
||||
|
||||
|
||||
[color "status"]
|
||||
|
||||
added = green bold
|
||||
branch = cyan dim bold reverse
|
||||
changed = yellow bold
|
||||
header = white
|
||||
localBranch = green bold
|
||||
nobranch = red dim bold reverse
|
||||
remoteBranch = cyan bold
|
||||
unmerged = red dim bold reverse
|
||||
untracked = blue bold
|
||||
|
||||
|
||||
|
||||
[commit]
|
||||
|
||||
# Set this in ~/.config/git/config_local to enable signing
|
||||
#
|
||||
# gpgSign = true
|
||||
|
||||
# Strip comment lines and excess whitespace from the message
|
||||
cleanup = strip
|
||||
|
||||
template = ~/.config/git/commit_msg_template.txt
|
||||
|
||||
# Show the staged diff below the commit message template in the editor
|
||||
verbose = true
|
||||
|
||||
|
||||
|
||||
[core]
|
||||
|
||||
excludesfile = ~/.config/git/ignore
|
||||
pager = less --chop-long-lines --ignore-case --LONG-PROMPT --no-init --quit-if-one-screen --RAW-CONTROL-CHARS --status-column
|
||||
|
||||
# Speed up `git status` by caching untracked files
|
||||
untrackedCache = true
|
||||
|
||||
# Flag tabs in the indentation but allow spaces before tabs
|
||||
whitespace = -space-before-tab,tab-in-indent
|
||||
|
||||
|
||||
|
||||
[diff]
|
||||
|
||||
# Better diff hunks for reordered code than the default "myers"
|
||||
algorithm = histogram
|
||||
|
||||
# Color lines that are merely moved around differently
|
||||
# from actually added/removed ones
|
||||
# => "default" is a synonym for the "zebra" mode
|
||||
colorMoved = default
|
||||
|
||||
# Show "i/" (index) and "w/" (working tree) etc. instead of "a/" and "b/"
|
||||
mnemonicPrefix = true
|
||||
|
||||
# Detect renamed files instead of showing delete-plus-add
|
||||
renames = true
|
||||
|
||||
# Show submodule changes as their list of commits
|
||||
submodule = log
|
||||
|
||||
|
||||
|
||||
[fetch]
|
||||
|
||||
# Auto-remove remote-tracking refs deleted on the remote
|
||||
prune = true
|
||||
|
||||
|
||||
|
||||
[help]
|
||||
|
||||
# Suggest a command on a typo and ask before running it
|
||||
autocorrect = prompt
|
||||
|
||||
|
||||
|
||||
[init]
|
||||
|
||||
defaultBranch = main
|
||||
|
||||
|
||||
|
||||
[merge]
|
||||
|
||||
# Show the common ancestor's version inside conflict markers,
|
||||
# minus the lines that both sides agree on
|
||||
conflictstyle = zdiff3
|
||||
|
||||
# Refuse `git merge` if a merge commit would be needed
|
||||
# => Use `git menoff` to create one on purpose
|
||||
ff = only
|
||||
|
||||
|
||||
|
||||
[pull]
|
||||
|
||||
# Replay local commits on top of the fetched branch
|
||||
# and do not create merge
|
||||
rebase = true
|
||||
|
||||
# Guard: A merge-style pull must be a fast-forward
|
||||
ff = only
|
||||
|
||||
|
||||
|
||||
[push]
|
||||
|
||||
# Auto-create the upstream on the first push of a new branch
|
||||
autoSetupRemote = true
|
||||
|
||||
# Push the current branch to its upstream
|
||||
default = upstream
|
||||
|
||||
# Refuse to push if a submodule's commits are unpushed
|
||||
recursesubmodules = check
|
||||
|
||||
|
||||
|
||||
[rebase]
|
||||
|
||||
# Auto-apply "fixup!" commits in every interactive rebase
|
||||
autosquash = true
|
||||
|
||||
# Stash away and restore local changes around a rebase
|
||||
autostash = true
|
||||
|
||||
# Also move stacked branches that point into the rebased range
|
||||
updateRefs = true
|
||||
|
||||
|
||||
|
||||
[rerere]
|
||||
|
||||
# Record conflict resolutions and reuse them when they reappear
|
||||
enabled = true
|
||||
|
||||
|
||||
|
||||
[tag]
|
||||
|
||||
# Sort "v1.10" after "v1.9"
|
||||
sort = version:refname
|
||||
|
||||
|
||||
|
||||
[url "ssh://git@bitbucket.org/"]
|
||||
|
||||
# Enables, for example, `git clone bb:atlassian/aui`
|
||||
insteadOf = bb:
|
||||
|
||||
# Push always via SSH, even for HTTPS-based remotes
|
||||
pushInsteadOf = bbh:
|
||||
pushInsteadOf = https://bitbucket.org/
|
||||
|
||||
[url "https://bitbucket.org/"]
|
||||
|
||||
# The HTTPS fallback on machines without SSH keys
|
||||
insteadOf = bbh:
|
||||
|
||||
|
||||
[url "ssh://git@github.com/"]
|
||||
|
||||
# Enables, for example, `git clone gh:torvalds/linux`
|
||||
insteadOf = gh:
|
||||
|
||||
# Push always via SSH, even for HTTPS-based remotes
|
||||
pushInsteadOf = ghh:
|
||||
pushInsteadOf = https://github.com/
|
||||
|
||||
[url "https://github.com/"]
|
||||
|
||||
# The HTTPS fallback on machines without SSH keys
|
||||
insteadOf = ghh:
|
||||
|
||||
|
||||
[url "ssh://git@gitlab.com/"]
|
||||
|
||||
# Enables, for example, `git clone gl:gitlab-org/gitlab`
|
||||
insteadOf = gl:
|
||||
|
||||
# Push always via SSH, even for HTTPS-based remotes
|
||||
pushInsteadOf = glh:
|
||||
pushInsteadOf = https://gitlab.com/
|
||||
|
||||
[url "https://gitlab.com/"]
|
||||
|
||||
# The HTTPS fallback on machines without SSH keys
|
||||
insteadOf = glh:
|
||||
|
||||
|
||||
[url "ssh://git@git.webartifex.de/"]
|
||||
|
||||
# Enables, for example, `git clone wa:alexander/dotfiles`
|
||||
insteadOf = wa:
|
||||
|
||||
# Push always via SSH, even for HTTPS-based remotes
|
||||
pushInsteadOf = wah:
|
||||
pushInsteadOf = https://code.webartifex.de/
|
||||
|
||||
[url "https://code.webartifex.de/"]
|
||||
|
||||
# The HTTPS fallback on machines without SSH keys
|
||||
insteadOf = wah:
|
||||
|
||||
|
||||
|
||||
[user]
|
||||
|
||||
# Set this in ~/.config/git/config_local to enable committing and signing
|
||||
#
|
||||
# name = ...
|
||||
# email = ...
|
||||
# signingKey = ...
|
||||
|
||||
|
||||
|
||||
[include]
|
||||
|
||||
path = ~/.config/git/config_local
|
||||
6
.config/git/ignore
Normal file
6
.config/git/ignore
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Generic temporary files
|
||||
*.backup
|
||||
*.bak
|
||||
*.orig
|
||||
*.temp
|
||||
*.tmp
|
||||
|
|
@ -3,9 +3,6 @@
|
|||
# Aliases used in all kinds of shells
|
||||
|
||||
|
||||
# Manage the bare `git` repository in ~/ holding the dotfiles
|
||||
alias dotfiles='git --git-dir=$DOTFILES_DIR --work-tree=$HOME'
|
||||
|
||||
|
||||
|
||||
forget() { # the current history and restore $HISTFILE from disk
|
||||
|
|
@ -72,3 +69,31 @@ alias la='ls --classify --format=vertical --almost-all'
|
|||
alias lal='ls --classify --format=long --almost-all'
|
||||
alias ll='ls --classify --format=long'
|
||||
alias lla='lal'
|
||||
|
||||
|
||||
|
||||
if _command_exists git; then
|
||||
|
||||
# Manage the bare `git` repository in ~/ holding the dotfiles
|
||||
alias dotfiles='git --git-dir=$DOTFILES_DIR --work-tree=$HOME'
|
||||
|
||||
alias g='git'
|
||||
|
||||
# Create "g" prefixed aliases from the "real" aliases
|
||||
# defined under "[alias]" in ~/.config/git/config,
|
||||
# for example, `gap` for `git add --patch`
|
||||
#
|
||||
# Interactive shells only: This spawns a `git` process,
|
||||
# which every non-interactive `bash` via `$BASH_ENV`
|
||||
# (e.g., ":!cmd" in `vi`) would pay on each command
|
||||
# => There, `g ap` works just as well as `gap`
|
||||
case $- in
|
||||
*i*)
|
||||
for al in $(git aliases --real); do
|
||||
eval "alias g$al='git $al'"
|
||||
done
|
||||
unset al
|
||||
;;
|
||||
esac
|
||||
|
||||
fi
|
||||
|
|
|
|||
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 }'
|
||||
4
.zshrc
4
.zshrc
|
|
@ -91,6 +91,10 @@ zstyle ':completion:*:messages' format '%d'
|
|||
zstyle ':completion:*:warnings' format 'No matches for: %d'
|
||||
zstyle ':completion:*' group-name ''
|
||||
|
||||
# Make `git` sub-commands in ~/.local/bin completable
|
||||
zstyle ':completion:*:*:git:*' user-commands \
|
||||
${${${(M)${(k)commands}:#git-*}#git-}:#(receive-pack|upload-pack|upload-archive|shell)}
|
||||
|
||||
|
||||
# Jump to frequently used directories with `z <fragment>`
|
||||
if _command_exists zoxide; then
|
||||
|
|
|
|||
Loading…
Reference in a new issue