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
|
# Enable tab completion
|
||||||
|
|
||||||
if ! shopt -oq posix; then
|
if ! shopt -oq posix; then
|
||||||
if [ -f /usr/share/bash-completion/bash_completion ]; then
|
if [ -f /usr/share/bash-completion/bash_completion ]; then
|
||||||
. /usr/share/bash-completion/bash_completion
|
. /usr/share/bash-completion/bash_completion
|
||||||
|
|
@ -78,68 +77,6 @@ if ! shopt -oq posix; then
|
||||||
fi
|
fi
|
||||||
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>`
|
# Jump to frequently used directories with `z <fragment>`
|
||||||
if _command_exists zoxide; then
|
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
|
# 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
|
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 lal='ls --classify --format=long --almost-all'
|
||||||
alias ll='ls --classify --format=long'
|
alias ll='ls --classify --format=long'
|
||||||
alias lla='lal'
|
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:*:warnings' format 'No matches for: %d'
|
||||||
zstyle ':completion:*' group-name ''
|
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>`
|
# Jump to frequently used directories with `z <fragment>`
|
||||||
if _command_exists zoxide; then
|
if _command_exists zoxide; then
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue