Skip to content

Instantly share code, notes, and snippets.

@alexanderdombroski
Last active October 9, 2025 16:35
Show Gist options
  • Save alexanderdombroski/ddac491daeff48c5f1346ba2960462fa to your computer and use it in GitHub Desktop.
Save alexanderdombroski/ddac491daeff48c5f1346ba2960462fa to your computer and use it in GitHub Desktop.
Adds extra git commands.
[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"

Alias Command List

  • git alist shows this file.
  • git aliases shows all aliases in collumns.

Commit Tooling

  • git amend commits with --no-edit and --amend. Accepts extra flags too.
  • git fixup <commit> commits staged changes as aa rebase, and starts an interactive rebase on the target commit if it would not fail.
  • git savepoint makes a WIP commit with current timestamp.
  • git co-commit <username> commits with a co-authorship description.

Merge Helpers

  • git can-merge <branch> shows if a conflict would occur.
  • git conflict <branch> shows all conflicts that would occur in a merge.
  • git abort and git continue are useful in conflict resolution situations.
  • git lines <branch> <ignore-file> <ignore-file> ...* shows the number of lines changed.
  • git yank is a force pull for the current branch. It commits a WIP commit and/or backup branch if commits don't exist in remote. Then it resets the local branch to match the remote. Useful way to handle a collaborator's force push.

Easy Config

  • git init-github creates a github repository matching the root folder name, sets the origin remote, and pushes.
  • git exclude and git include allows you to ignore files locally without modifying the .gitignore file.
  • git remote-set <name> <url> uses git remote 'set-url' or 'add', whichever would not error out.
  • git remote-default shows whether the remote default branch is 'main', 'master', etc.

Branching

  • git dates shows all branches and date of last commit.
  • git old shows age of each branch and if it has a remote branch that was deleted.
  • git auto-prune deletes any already merged branches.
  • git rebranch If conflicts won't be too bad, stashes changes and cherry-picks commits to a new branch of the same name branched from main.
  • git track <branch> copies a remote branch to a local one and sets the origin upstream.

Github

  • git github opens the github repo.
  • git pr-info tells you # of lines changed, # of commits, if can-merge, and commit summary.
  • git pr-view <commit> opens the pr for the given commit or current branch if no commit provided.

Logging

Each of these accept extra log flags, such as --oneline.

  • git log-n shows commits not in origin/main.
  • git log-me shows your commits.
  • git log-t shows logs with tags.
  • git log-b shows commits exclusive to your current branch.

Utility

  • git pwd shows repo root path.
  • git whoami shows github username
  • git tree shows all files in a tree.
  • git files <commit> shows the files edited by the given commit.
  • git repo-name prints '<owner>/<reponame>'.
  • git languages prints the percentages of languages for this repo.
  • git churn shows you the 25 most edited files.
  • git gif uses ffmpeg to convert .mov screen recordings to gifs.

gh command wrapper

Use any gh commands: git <command> <subcommand>

Setup a LAN git server

Many of these commands require an argument of name. This is the last part of the url ie 'my-repo.git'.

  • git server-start <remote> creates a bare .git folder in the parent folder of your repo and hosts it over LAN. Optionally specify a remote name to update.
  • git server-addr shows what ip address LAN is hosting from.
  • git server-logs open the logs of the server.
  • git server-test <name> attempts to run git ls-remote using the server url.
  • git server-kill kills all git daemon processes (meaning the server).
  • git server-clone <name> clone the repo
  • git server-join sets the origin remote of the current repo to the LAN url.

Interacting with this gist

  • git aliases-gist replace this with your gist id if you fork the gists.
  • git aliases-export overwrite your backed up aliases to your global aliases.
  • git aliases-import deletes all your aliases and replaces them with the backup gist.
  • git aliases-docs edit this markdown file.
@alexanderdombroski
Copy link
Author

Add all of these aliases to your .gitconfig (will overwrite aliases of the same name)

gh api /gists/ddac491daeff48c5f1346ba2960462fa --jq '.files[".gitconfig"].content' \
| git config -f /dev/stdin --get-regexp '^alias\.' \
| while read -r key value; do git config --global "$key" "$value"; done

If you want to keep the formatting, you can remove all existing global git aliases and append these to your config

git config --global --get-regexp '^alias\.' \
  | awk '{print $1}' \
  | xargs -r -L1 git config --global --unset

gh api /gists/ddac491daeff48c5f1346ba2960462fa \
  --jq '.files[".gitconfig"].content' \
  >> ~/.gitconfig

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment