|
[alias] |
|
alist = "!gh gist view $(git aliases-gist) --filename command-list.md" |
|
conflict = "!f() { \ |
|
TO=$1; \ |
|
TMP=$(mktemp); \ |
|
git merge-tree $(git merge-base HEAD $TO) HEAD $TO > $TMP; \ |
|
if grep -q '<<<<<<<' $TMP; then \ |
|
awk ' \ |
|
/<<<<<<< / { in_conflict=1; print; next } \ |
|
/>>>>>>> / { print; in_conflict=0; next } \ |
|
in_conflict { print } \ |
|
/^=======/ { if (in_conflict) print } \ |
|
' $TMP | \ |
|
sed -e $'s/<<<<<<<.*/\\033[31m&\\033[0m/' \ |
|
-e $'s/=======.*/\\033[33m&\\033[0m/' \ |
|
-e $'s/>>>>>>>.*/\\033[32m&\\033[0m/'; \ |
|
echo \"❌ $(grep -c '^+=======$' "$TMP") merge conflicts would occur!\"; \ |
|
else \ |
|
echo '✅ No conflicts. Safe to merge.'; \ |
|
fi; \ |
|
rm $TMP; \ |
|
}; f" |
|
can-merge = "!f() { \ |
|
TO=$1; \ |
|
TMP=$(mktemp); \ |
|
git merge-tree $(git merge-base HEAD $TO) HEAD $TO > $TMP; \ |
|
if grep -q '<<<<<<<' $TMP; then \ |
|
echo '❌ Merge conflict would occur!'; \ |
|
exit 1; \ |
|
else \ |
|
echo '✅ No conflicts. Safe to merge.'; \ |
|
fi; \ |
|
rm $TMP; \ |
|
}; f" |
|
continue = "!f() { \ |
|
gitdir=$(git rev-parse --git-dir); \ |
|
if [ -d \"$gitdir/rebase-merge\" ] || [ -d \"$gitdir/rebase-apply\" ]; then \ |
|
git rebase --continue; \ |
|
elif [ -f \"$gitdir/MERGE_HEAD\" ]; then \ |
|
git merge --continue; \ |
|
elif [ -f \"$gitdir/CHERRY_PICK_HEAD\" ]; then \ |
|
git cherry-pick --continue; \ |
|
elif [ -f \"$gitdir/REVERT_HEAD\" ]; then \ |
|
git revert --continue; \ |
|
else \ |
|
echo '✅ Nothing to continue.'; \ |
|
fi; \ |
|
}; f" |
|
abort = "!f() { \ |
|
gitdir=$(git rev-parse --git-dir); \ |
|
if [ -d \"$gitdir/rebase-merge\" ] || [ -d \"$gitdir/rebase-apply\" ]; then \ |
|
git rebase --abort; \ |
|
echo '✅ Aborted the rebase.'; \ |
|
elif [ -f \"$gitdir/MERGE_HEAD\" ]; then \ |
|
git merge --abort; \ |
|
elif [ -f \"$gitdir/CHERRY_PICK_HEAD\" ]; then \ |
|
git cherry-pick --abort; \ |
|
else \ |
|
echo '✅ Nothing to abort.'; \ |
|
fi; \ |
|
}; f" |
|
pwd = "!git rev-parse --show-toplevel" |
|
whoami = "!gh api user --jq .login" |
|
exclude = "!f() { \ |
|
path=\"$1\"; \ |
|
exclude_path=\"$(git rev-parse --git-path info/exclude)\"; \ |
|
if grep -Fxq \"$path\" \"$exclude_path\"; then \ |
|
echo \"⚠️ Already excluded: $path\"; \ |
|
else \ |
|
echo \"$path\" >> \"$exclude_path\"; \ |
|
echo \"❎ Excluded: $path\"; \ |
|
fi; \ |
|
}; f" |
|
include = "!f() { \ |
|
exclude_path=\"$(git rev-parse --git-path info/exclude)\"; \ |
|
if grep -Fxq \"$1\" \"$exclude_path\"; then \ |
|
grep -Fxv \"$1\" \"$exclude_path\" > \"$exclude_path.tmp\" && mv \"$exclude_path.tmp\" \"$exclude_path\"; \ |
|
echo \"❎ Removed: $1\"; \ |
|
else \ |
|
echo \"⚠️ Not found in exclude: $1\"; \ |
|
fi; \ |
|
}; f" |
|
tree = "!f() { \ |
|
git ls-files --cached --others --exclude-standard | sort -f | awk ' \ |
|
BEGIN { FS=\"/\" } \ |
|
{ \ |
|
n = split($0, parts, \"/\"); \ |
|
path = \"\"; \ |
|
for (i = 1; i < n; i++) { \ |
|
path = (i == 1 ? parts[i] : path \"/\" parts[i]); \ |
|
if (!(path in seen)) { \ |
|
seen[path] = 1; \ |
|
indent = \"\"; \ |
|
for (j = 1; j < i; j++) indent = indent \"│ \"; \ |
|
print indent \"├── \\033[34m\" parts[i] \"/\\033[0m\"; \ |
|
} \ |
|
} \ |
|
indent = \"\"; \ |
|
for (j = 1; j < n; j++) indent = indent \"│ \"; \ |
|
print indent bend \"├── \" parts[n]; \ |
|
}'; \ |
|
}; f" |
|
aliases = "!git config --get-regexp '^alias\\.' | cut -d' ' -f1 | sed 's/^alias\\.//' | sort | column -c $(tput cols)" |
|
savepoint = !bash -c 'git add -A && git commit -m \"WIP $(date \"+%Y-%m-%d %I:%M %p\")\"' |
|
dates = !git for-each-ref --sort=-committerdate --format='%(committerdate:short) %(refname:short)' refs/heads/ |
|
lines = "!f() { \ |
|
branch=\"$1\"; shift; excludes=\"\"; \ |
|
for f in \"$@\"; do excludes=\"$excludes :(exclude)$f\"; done; \ |
|
git diff --shortstat \"$branch\"...HEAD $excludes; \ |
|
}; f" |
|
remote-default = "!f() { \ |
|
git rev-parse --abbrev-ref origin/HEAD >/dev/null 2>&1 || git remote set-head origin -a >/dev/null 2>&1; \ |
|
git rev-parse --abbrev-ref origin/HEAD; \ |
|
}; f | sed 's|origin/||'" |
|
remote-set = "!f() { \ |
|
if git remote get-url $1 >/dev/null 2>&1; then \ |
|
git remote set-url $@; \ |
|
else \ |
|
git remote add $@; \ |
|
fi; \ |
|
}; f" |
|
log-b = "!sh -c 'git log $(git remote-default)..HEAD $@' -" |
|
log-me = "!sh -c 'git log --author=\"$(git config user.name)\" $@' -" |
|
log-t = "!sh -c 'git log --tags --simplify-by-decoration $@' -" |
|
log-n = "!sh -c 'git log origin/$(git remote-default)..HEAD $@' -" |
|
old = "!f() { \ |
|
printf \"Fetching and Pruning...\"; \ |
|
git fetch --prune >/dev/null 2>&1; \ |
|
printf \"\\r\"; \ |
|
DEFAULT=$(git remote-default); \ |
|
CURRENT=$(git symbolic-ref --short HEAD); \ |
|
MAX_LEN=$(git branch --format='%(refname:short)' | awk '{ print length }' | sort -nr | head -1); \ |
|
echo '---------------------------- Branches ----------------------------'; \ |
|
git for-each-ref --sort=-committerdate --format='%(refname:short)%09%(committerdate:relative)%09%(upstream:track)' refs/heads/ \ |
|
| while IFS=$'\t' read BRANCH DATE TRACK; \ |
|
do \ |
|
AHEAD=$(git rev-list --count origin/$DEFAULT..$BRANCH 2>/dev/null); \ |
|
BEHIND=$(git rev-list --count $BRANCH..origin/$DEFAULT 2>/dev/null); \ |
|
[[ "$TRACK" == *gone* ]] && MERGED=\"MERGED by $(git log -1 --format="%cn" "$BRANCH")\" || MERGED=''; \ |
|
if [ \"$BRANCH\" = \"$CURRENT\" ]; then STATUS='*'; \ |
|
elif git worktree list | grep -q \"\\[$BRANCH\\]\"; then STATUS='+'; \ |
|
else STATUS=' '; fi; \ |
|
printf \"%s %-*s | %-16s | +%-3s -%-3s | %s\\n\" \"$STATUS\" \"$MAX_LEN\" \"$BRANCH\" \"$DATE\" \"$AHEAD\" \"$BEHIND\" \"$MERGED\"; \ |
|
done | git column; \ |
|
}; f" |
|
auto-prune = "!f() { \ |
|
if ! git diff --quiet || ! git diff --cached --quiet; then \ |
|
echo '❌ Cannot safely switch branches'; \ |
|
exit 1; \ |
|
fi; \ |
|
MAIN=$(git remote-default); \ |
|
git switch $MAIN >/dev/null 2>&1; \ |
|
git fetch --prune && git pull; \ |
|
git branch --merged $MAIN | grep -vE \"^\\*|master\" | xargs -r git branch -d; \ |
|
}; f" |
|
files = "!sh -c 'git show --name-only --pretty="" $@' -" |
|
amend = "!sh -c 'git commit --amend --no-edit $@' -" |
|
fixup = "!f() { \ |
|
TARGET=$(git rev-parse $1); \ |
|
git commit --fixup=\"$TARGET\" || exit 1; \ |
|
FIXUP=$(git rev-parse HEAD); \ |
|
for COMMIT in $(git rev-list --reverse \"$TARGET^\"..HEAD); do \ |
|
PATCH=$(git diff \"$COMMIT\"^ \"$COMMIT\"); \ |
|
if ! echo \"$PATCH\" | git apply --check --3way -q; then \ |
|
SHORT=$(git rev-parse --short $COMMIT); \ |
|
echo \"❌ Rebase conflict would occur!\"; \ |
|
git reset HEAD~1 --soft; \ |
|
exit 1; \ |
|
fi; \ |
|
done; \ |
|
git rebase -i --autosquash \"$TARGET^\"; \ |
|
}; f" |
|
languages = "!f() { \ |
|
DATA=$(gh api repos/$(git repo-name)/languages); \ |
|
PERCENT=$(perl -MJSON::PP -0777 -e \" \ |
|
use strict; \ |
|
use warnings; \ |
|
my \\$data = decode_json(join(\\\"\\\", <STDIN>)); \ |
|
my \\$total = 0; \ |
|
\\$total += \\$_ for values %\\$data; \ |
|
my @out; \ |
|
for my \\$k (keys %\\$data) { \ |
|
my \\$p = int(\\$data->{\\$k} / \\$total * 100 + 0.5); \ |
|
push @out, \\\"\\$k: \\$p%\\\"; \ |
|
} \ |
|
print join(\\\" \\\", @out), \\\"\\n\\\"; \ |
|
\" <<< \"$DATA\"); \ |
|
echo \"$PERCENT\"; \ |
|
}; f" |
|
repo-name = "!gh repo view --json nameWithOwner -q .nameWithOwner" |
|
init-github = "!f() { \ |
|
git remote show origin 2>/dev/null && echo 'origin remote already exists!' && exit 1; \ |
|
ROOT=$(git pwd) && cd $ROOT || exit 1; \ |
|
REPO=$(basename $ROOT); \ |
|
gh repo create $REPO --public --source=. --remote=origin && git push; \ |
|
}; f" |
|
co-commit = "!f() { \ |
|
if [ -z \"$1\" ]; then \ |
|
echo 'Usage: git co-commit <github-username>'; \ |
|
exit 1; \ |
|
fi; \ |
|
USER=\"$1\"; \ |
|
if ! NAME=$(gh api users/\"$USER\" --jq .name 2>/dev/null); then \ |
|
echo \"Could not retrieve name for user $USER\"; \ |
|
exit 1; \ |
|
fi; \ |
|
if ! EMAIL=$(gh api users/\"$USER\" --jq .email 2>/dev/null); then \ |
|
echo \"$NAME has a private email.\"; \ |
|
else \ |
|
echo \"$EMAIL\"; \ |
|
git commit --edit -m \"<insert summary>\" -m \"Co-authored-by: $NAME <$EMAIL>\"; \ |
|
fi; \ |
|
}; f" |
|
pr-info = "!f() { \ |
|
DEST=\"$(git remote-default)\"; \ |
|
git fetch origin $DEST >/dev/null 2>&1; \ |
|
echo \"-------------------- New PR Summary --------------------\"; \ |
|
echo \"$(git rev-list --count origin/$DEST..HEAD) commits $(git lines origin/$DEST)\"; \ |
|
git can-merge origin/$DEST; \ |
|
echo; \ |
|
git log-n --oneline; \ |
|
}; f" |
|
pr-view = "!f() { \ |
|
if [ -z \"$1\" ]; then \ |
|
if gh pr view >/dev/null 2>&1; then \ |
|
gh pr view --web; \ |
|
else \ |
|
echo \"No PR found for current branch. Provide a commit SHA.\"; \ |
|
fi; \ |
|
else \ |
|
COMMIT=\"$(git rev-parse --short \"$1\")\"; \ |
|
PRS=$(gh pr list --search \"$COMMIT\" --state all --json number --jq \"(.[].number)\"); \ |
|
if [ -z \"$PRS\" ]; then \ |
|
echo \"No PR found for commit: $COMMIT\"; \ |
|
else \ |
|
if [ \"$(echo \"$PRS\" | wc -l | xargs)\" -gt 1 ]; then \ |
|
PRS=$(echo \"$PRS\"); \ |
|
for PR in $PRS; do \ |
|
TITLE=$(gh pr view $PR --json title --jq '.title'); \ |
|
echo \"gh pr view $PR --web | $TITLE\"; \ |
|
done; \ |
|
else \ |
|
gh pr view \"$PRS\" --web; \ |
|
fi; \ |
|
fi; \ |
|
fi; \ |
|
}; f" |
|
github = "!gh repo view --web" |
|
rebranch = "!f() { \ |
|
git stash -u >/dev/null 2>&1; \ |
|
MAIN=$(git remote-default); \ |
|
if ! git can-merge $MAIN; then \ |
|
git stash pop >/dev/null 2>&1; \ |
|
exit 1; \ |
|
fi; \ |
|
BRANCH=$(git symbolic-ref --short HEAD); \ |
|
git switch $MAIN; \ |
|
git pull || exit 1; \ |
|
git switch -; \ |
|
TEMP=$(uuidgen); \ |
|
git switch -c $TEMP; \ |
|
git switch $MAIN; \ |
|
git branch -D $BRANCH; \ |
|
git switch -c $BRANCH; \ |
|
for COMMIT in $(git rev-list --reverse \"$MAIN..$TEMP\"); do \ |
|
git cherry-pick $COMMIT || (echo \"❌ cherry pick failed at $COMMIT\" && exit 1); \ |
|
done; \ |
|
git branch -D $TEMP; \ |
|
git stash pop >/dev/null 2>&1; \ |
|
}; f" |
|
track = "!sh -c 'git fetch origin && git checkout --track origin/$1' -" |
|
churn = "!f() { \ |
|
git -p log --all -M -C --name-only --format='format:' \ |
|
| sort | grep -v '^$' | uniq -c | sort -r \ |
|
| awk 'BEGIN {print count,file} {print $1 , $2}' \ |
|
| head -n 25; \ |
|
}; f" |
|
yank = "!f() { \ |
|
BRANCH=$(git rev-parse --abbrev-ref HEAD); \ |
|
if [ \"$BRANCH\" = \"HEAD\" ]; then \ |
|
echo 'Error: You are in a detached HEAD. Cannot yank.'; \ |
|
exit 1; \ |
|
fi; \ |
|
if ! git rev-parse --abbrev-ref --symbolic-full-name \"$BRANCH\"@{u} >/dev/null 2>&1; then \ |
|
echo \"Error: '$BRANCH' has no upstream remote. Cannot yank.\"; \ |
|
exit 1; \ |
|
fi; \ |
|
git fetch origin \"$BRANCH\" --force; \ |
|
git savepoint; \ |
|
AHEAD=$(git rev-list --count \"origin/$BRANCH..$BRANCH\"); \ |
|
if [ \"$AHEAD\" -ne 0 ]; then \ |
|
BACKUP=\"${BRANCH}-old-$(uuidgen)\"; \ |
|
git branch \"$BACKUP\"; \ |
|
echo \"Created backup branch $BACKUP\"; \ |
|
fi; \ |
|
git reset --hard \"origin/$BRANCH\"; \ |
|
}; f" |
|
aliases-gist = "!echo 'ddac491daeff48c5f1346ba2960462fa'" # Add your backup gist id here |
|
aliases-export = "!f() { \ |
|
ALIST=$(awk 'p && /^\\[/{exit} /^\\[alias\\]/{p=1} p' ~/.gitconfig); \ |
|
gh api -X PATCH /gists/$(git aliases-gist) -f files[.gitconfig][content]=\"$ALIST\"; \ |
|
}; f" |
|
aliases-import = "!f() { \ |
|
GIST=$(git aliases-gist); \ |
|
git config --global --get-regexp '^alias\\.' \ |
|
| awk '{print $1}' | xargs -r -L1 git config --global --unset; \ |
|
gh api /gists/\"$GIST\" --jq '.files[\".gitconfig\"].content' >> ~/.gitconfig; \ |
|
}; f" |
|
aliases-docs = "!gh gist edit $(git aliases-gist) -f command-list.md" |
|
server-addr = "!ifconfig | awk '/^en[0-9]/ {iface=$1} /inet / && $2 !~ /^127\\./ {print $2; exit}'" |
|
server-start = "!f() { \ |
|
cd $(git pwd) || exit 1; \ |
|
NAME=$(basename $(pwd)); \ |
|
cd ..; \ |
|
REPO=\"$(pwd)/$NAME.git\"; \ |
|
echo $REPO; \ |
|
rm -rf $REPO || exit 1; \ |
|
git clone --bare \"$NAME\" \"$REPO\" || exit 1; \ |
|
LAN_IP=$(git server-addr); \ |
|
nohup git daemon --verbose --export-all --enable=receive-pack --base-path=$(pwd) \ |
|
--reuseaddr --strict-paths $REPO > /tmp/git-daemon.log 2>&1 & \ |
|
REMOTE_URL=\"git://$LAN_IP:9418/$NAME.git\"; \ |
|
echo \"Serving repository from $REMOTE_URL\"; \ |
|
cd - > /dev/null; \ |
|
if [ -n \"$1\" ]; then \ |
|
git remote-set $1 $REMOTE_URL; \ |
|
fi; \ |
|
}; f" |
|
server-kill = "!f() { \ |
|
PIDS=$(ps -eo pid,comm | grep -E 'git-daemo|git daemon' | awk '{print $1}'); \ |
|
for PID in $PIDS; do \ |
|
echo \"Killing: $PID\"; \ |
|
done; \ |
|
pkill -f \"git-daemo\"; \ |
|
pkill -f \"git daemon\"; \ |
|
if lsof -i :9418; then \ |
|
echo 'Error: some processes not killed'; \ |
|
fi; \ |
|
}; f" |
|
server-test = "!f() { \ |
|
REMOTE=\"git://$(git server-addr):9418/$1\"; \ |
|
if git ls-remote $REMOTE > /dev/null; then \ |
|
echo \"Server running at: $REMOTE\"; \ |
|
else exit 1; fi; \ |
|
}; f" |
|
server-logs = "!tail -f /tmp/git-daemon.log" |
|
server-clone = "!f() { \ |
|
REMOTE=\"git://$(git server-addr):9418/$1\"; \ |
|
if git server-test $1; then \ |
|
git clone $REMOTE; \ |
|
fi; \ |
|
}; f" |
|
server-join = "!f() { \ |
|
BASE=$(basename $(git pwd)); \ |
|
REMOTE=\"git://$(git server-addr):9418/$BASE.git\"; \ |
|
if git server-test \"$BASE.git\"; then \ |
|
git remote-set origin $REMOTE; \ |
|
echo \"origin updated: (fetch & push)\"; \ |
|
fi; \ |
|
}; f" |
|
auth = "!sh -c 'gh auth $@' -" |
|
browse = "!sh -c 'gh browse $@' -" |
|
codespace = "!sh -c 'gh codespace $@' -" |
|
gist = "!sh -c 'gh gist $@' -" |
|
issue = "!sh -c 'gh issue $@' -" |
|
org = "!sh -c 'gh org $@' -" |
|
pr = "!sh -c 'gh pr $@' -" |
|
project = "!sh -c 'gh project $@' -" |
|
release = "!sh -c 'gh release $@' -" |
|
repo = "!sh -c 'gh repo $@' -" |
|
gif = "!f() { \ |
|
cd ~/Downloads; \ |
|
for FILE in *.mov; do \ |
|
ffmpeg -i \"$FILE\" -filter_complex \ |
|
\"[0:v] fps=15,scale=1080:-1:flags=lanczos [x]; [x] palettegen [p]; [x][p] paletteuse=dither=bayer\" \ |
|
-y \"${FILE%.*}.gif\"; \ |
|
done \ |
|
}; f" |
Add all of these aliases to your .gitconfig (will overwrite aliases of the same name)
If you want to keep the formatting, you can remove all existing global git aliases and append these to your config