Last active
March 10, 2024 16:22
-
-
Save huaminghuangtw/24b029222e1152ca34138521907c15e3 to your computer and use it in GitHub Desktop.
My Git Configuration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[user] | |
name = Hua-Ming Huang | |
email = huaming.huang.tw@gmai.com | |
[github] | |
user = huaminghuangtw | |
tokentype = ssh | |
[include] | |
path = ~/.git-sendemail | |
[help] | |
autocorrect = 1 | |
[init] | |
defaultBranch = main | |
[push] | |
default = current | |
[pull] | |
default = current | |
rebase = true | |
[diff] | |
mnemonicprefix = true | |
ignoreSubmodules = dirty | |
[branch] | |
autosetuprebase = always | |
[apply] | |
# Detect whitespace errors when applying a patch | |
whitespace = fix | |
[url "https://github"] | |
insteadOf = git://github | |
[url "git@github.com:github"] | |
insteadOf = "https://github.com/github" | |
insteadOf = "github:github" | |
insteadOf = "git://github.com/github" | |
[url "git@github.com:"] | |
pushInsteadOf = "https://github.com/" | |
pushInsteadOf = "github:" | |
pushInsteadOf = "git://github.com/" | |
[url "git://github.com/"] | |
insteadOf = "github:" | |
[url "git@gist.github.com:"] | |
insteadOf = "gst:" | |
pushInsteadOf = "gist:" | |
pushInsteadOf = "git://gist.github.com/" | |
[url "git://gist.github.com/"] | |
insteadOf = "gist:" | |
[submodule] | |
recurse = true | |
[color] | |
ui = true | |
branch = auto | |
diff = auto | |
status = auto | |
[alias] | |
# See more examples in https://github.com/GitAlias/gitalias/blob/main/gitalias.txt | |
# Note: using the "!" prefix allows one to use any command and not just git commands in the alias | |
# Note: $@ means all command line parameters passed | |
# TODO: done, interactive (pull-force - make sure you "$ git checkout my_branch" first!) | |
### Short aliases for our frequent commands ### | |
c = clone --recursive # Clone a repository including all submodules | |
l = config -l --show-origin | |
f = fetch | |
p = push | |
# Run this before every commit to make sure you don’t need to use the "undo" commands | |
# (Show the difference between the latest commit and the current state) | |
d = !"git diff-index --quiet HEAD -- || clear; git --no-pager diff --patch-with-stat" | |
pt = push --tags | |
st = status -s | |
cp = cherry-pick | |
ca = !"git add . && git commit -m" | |
cm = commit -m | |
cg = config --global | |
gl = !"git cg -l" | |
ic = !"git add . && git commit --allow-empty -m 'Initial commit'" # Motivation: When you "git init" Git doesn't create a main branch for you by default until you commit something! | |
up = !"git checkout main && git fetch upstream && git rebase upstream/main && git submodule-update" # First run "git remote add upstream <url>" | |
rao = remote add origin | |
here = !"f() { git init && git rao ${1} && git pull origin main && return 0; }; f" | |
last = log -1 HEAD --stat | |
main = !"git checkout main && git up" | |
branches = branch -a | |
tags = tag -n1 --list | |
last-tag = describe --tags --abbrev=0 | |
stashes = stash list | |
remotes = remote -v | |
merge-test = "!f(){ git merge --no-commit --no-ff \"$1\"; git merge --abort; echo \"Merge aborted\"; };f " # Run this before merge to check for any conflicts | |
unmerged = diff --name-only --diff-filter=U | |
publish = !"git push --set-upstream origin $(git branch-name)" | |
unpublish = !"git push origin :$(git branch-name)"\ | |
sub-repo = !sh -c 'git filter-branch --prune-empty --subdirectory-filter $1 main' - | |
human = name-rev --name-only --refs=refs/heads/* | |
# Initalize a repo and immediately add an empty commit, which makes rebase easier. | |
start = !"f() { git init && git ic; }; f" # run "git github-create" after "git start" if necessary | |
# Create a remote repo on GitHub (GitHub token needed: https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token) | |
# Note: make sure you have no unstaged changes before running this command! | |
# Note: If not working, make sure the directory is not placed on cloud drive, then delete the newly created repo on GitHub and run $ git remote remove origin, and try again. | |
github-create = !"f() { \ | |
repo_name=\"$1\"; \ | |
invalid_credentials=0; \ | |
dir_name=`basename $(pwd)`; \ | |
if [ \"$repo_name\" = \"\" ]; then \ | |
echo \" Repo Name? (press enter to use \"$dir_name\")\"; \ | |
read repo_name; \ | |
fi; \ | |
if [ \"$repo_name\" = \"\" ]; then \ | |
repo_name=$dir_name; \ | |
fi; \ | |
username=$(git config github.user); \ | |
if [ \"$username\" = \"\" ]; then \ | |
echo \" ERROR - Could not find GitHub username, run 'git config --global github.user <username>'\"; \ | |
invalid_credentials=1; \ | |
fi; \ | |
token=$MY_GITHUB_PAT; \ | |
if [ \"$token\" = \"\" ]; then \ | |
echo \" ERROR - Could not find GitHub personal access token (PAT), run 'echo "export MY_GITHUB_PAT=<your_token>" >> ~/.zshenv'\"; \ | |
invalid_credentials=1; \ | |
fi; \ | |
type=$(git config github.tokentype); \ | |
if [ \"$type\" = \"ssh\" ]; then \ | |
conn_string="git@github.com:\"$username\"/\"$repo_name\".git"; \ | |
elif [ \"$type\" = \"http\" ]; then \ | |
conn_string="https://github.com/\"$username\"/\"$repo_name\".git"; \ | |
else \ | |
echo \" ERROR - Either token type was not entered correctly or is empty.\n It must be either 'ssh' or 'http'.\n Run git config --global github.tokentype <ssh|http>\"; \ | |
invalid_credentials=1; \ | |
fi; \ | |
if [ \"$invalid_credentials\" -eq \"1\" ]; then \ | |
return 1; \ | |
fi; \ | |
echo -n \" Creating GitHub repository '$repo_name' ...\"; \ | |
curl -u \"$username:$token\" https://api.github.com/user/repos -d '{\"name\":\"'$repo_name'\"}' > /dev/null 2>&1; \ | |
echo \" done.\"; \ | |
echo -n \" Pushing local code to GitHub ...\"; \ | |
git remote add origin \"$conn_string\" > /dev/null 2>&1; \ | |
git push origin main > /dev/null 2>&1; \ | |
echo \" done.\"; \ | |
}; f" | |
# conflict / merges | |
# Usage: git ours/theirs <the-path(s)-of-the-files/directories-to-checkout> | |
ours = "!f() { git checkout --ours $@ && git add $@ && git cm "Merge OURS version into '$@'"; }; f" | |
theirs = "!f() { git checkout --theirs $@ && git add $@ && git cm "Merge THEIRS version into '$@'"; }; f" | |
# merge two Git repositories | |
merge-repo = !"f() { \ | |
if [ $# -ne 1 ]; then \ | |
echo \"Usage: git merge-repo <path/to/another-project> \n\"; \ | |
echo \"If you want to put <another-project> into a subdirectory, run: \n\"; \ | |
echo \" cd <path/to/another-project> \"; \ | |
echo \" git filter-repo --to-subdirectory-filter <another-project> \n\"; \ | |
echo \"before 'git merge-repo ...' \"; \ | |
else \ | |
another_project_name=`basename ${1}`; \ | |
git remote add $another_project_name ${1}; \ | |
git fetch $another_project_name --tags; \ | |
git merge --allow-unrelated-histories $another_project_name/main; \ | |
git remote remove $another_project_name; \ | |
fi; \ | |
}; f" | |
### SHELL SCRIPTING ALIASES ### | |
# Get the top level directory name | |
top-name = rev-parse --show-toplevel | |
# Get the current branch name | |
branch-name = rev-parse --abbrev-ref HEAD | |
# Get the upstream branch name | |
upstream-name = !git for-each-ref --format='%(upstream:short)' $(git symbolic-ref -q HEAD) | |
### log ### | |
# View abbreviated SHA, description, and history graph of the latest 20 commits | |
oneline = log --graph --pretty=oneline -n 20 --abbrev-commit | |
changelog = log --pretty='- %s' > CHANGELOG.md # The "%s" corresponds to the commit title itself | |
lg1 = lg1-specific --all --simplify-by-decoration | |
lg2 = lg2-specific --all --simplify-by-decoration | |
lg3 = lg3-specific --all --simplify-by-decoration | |
lg1-specific = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(auto)%d%C(reset)' | |
lg2-specific = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(auto)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' | |
lg3-specific = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset) %C(bold cyan)(committed: %cD)%C(reset) %C(auto)%d%C(reset)%n'' %C(white)%s%C(reset)%n'' %C(dim white)- %an <%ae> %C(reset) %C(dim white)(committer: %cn <%ce>)%C(reset)' | |
# log like - we like this summarization our key performance indicators. Also aliased as 'log-like'. | |
ll = log --graph --topo-order --date-order --date=short --abbrev-commit --decorate --all --boundary --pretty=format:'%Cgreen%ad %Cred%h%Creset -%C(yellow)%d%Creset %s %Cblue[%cn]%Creset %Cblue%G?%Creset' | |
log-like = !"git ll" | |
# log like long - we like this summarization our key performance indicators. Also aliased as 'log-like-long'. | |
lll = log --graph --topo-order --date-order --date=iso8601-strict --no-abbrev-commit --abbrev=40 --decorate --all --boundary --pretty=format:'%Cgreen%ad %Cred%h%Creset -%C(yellow)%d%Creset %s %Cblue[%cn <%ce>]%Creset %Cblue%G?%Creset' | |
log-like-long = !"git lll" | |
graph = log --graph --all --simplify-by-decoration | |
hist = log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short | |
tree = log --graph --decorate --pretty=format:'%C(yellow)%h %Cred%cr %Cblue(%an)%C(cyan)%d%Creset %s' --abbrev-commit --all | |
### ls-files ### | |
# ls-files - show information about files in the index and the working tree; like Unix "ls" command. | |
ls = ls-files | |
# ls-ignored - list files that git has ignored. | |
ls-ignored = ls-files --others --i --exclude-standard | |
### grep-* ### | |
# Find text in any commit ever. | |
grep-all = !"f() { git rev-list --all | xargs git grep \"$@\"; }; f" | |
# Find text and group the output lines. A.k.a. 'gg'. | |
grep-group = grep --break --heading --line-number --color | |
### ALIAS MANAGEMENT ### | |
# Show our defined alias list | |
aliases = !"git config --get-regexp ^alias\\." | |
# Force an overwrite of local files with remote (main) branch | |
pull-force = !"git branch backup && git fetch --all && git reset --hard origin/${1-main}" | |
# Push to all remotes | |
push-to-all-remotes = !"git remote | xargs -I% -n1 git push %" | |
# Clear out the history of a git/github repository | |
clr = !"rm -rf .git" | |
clr-and-reinit = !"rm -rf .git && git start" | |
# Remove remote branch references that no longer exist, cleanup unnecessary git files, remove untracked files from the working tree and clear out stash | |
cleanup = !"git remote prune origin && git gc && git clean -dfx && git stash clear" | |
# Only for .gitconfig | |
edit-config = config --global --edit | |
update-config = "!f() { cd ~ && git add . && git commit -m 'Update .gitconfig' && git push ${1}; }; f" | |
# Only for .zshrc & .zsh_aliases | |
update-zsh = "!f() { cd ~ && git add . && git commit -m 'Update zsh configuration' && git push ${1}; }; f" | |
# Only for Windows PowerShell profile | |
update-PS-profile = "!f() { git add . && git commit -m 'Update PS profile' && git push ${1}; }; f" | |
# .gitignore related | |
ignore-unignored = !"git rm -r --cached --ignore-unmatch . && git add . && git commit -m '.gitignore fix' && git update-index --assume-unchanged .; git push;" | |
unignore-ignored = "!f() { git add -f ${1} && git add . && git commit -m '.gitignore fix' && git update-index --assume-unchanged .; git push; }; f" | |
### BRANCHES ### | |
# List all branches | |
br = branch -a | |
# List all branches sorted by last modified | |
b = "!git for-each-ref --sort='-authordate' --format='(%(authordate))%09%(objectname:short)%09%(refname)' refs/heads | sed -e 's-refs/heads/--'" | |
# Delete local or remote branches | |
del-local = branch --delete | |
del-remote = push origin --delete | |
# Remove branches that have already been merged into main or another branch provided as an argument | |
cleanup-merged-branches = "!f() { git branch --merged ${1:-main} | egrep -v \"(^\\*|${1:-main})\" | xargs — no-run-if-empty git branch -d; git remote -v update -p; }; f" | |
# Show GitHub repository url | |
url-github =!"echo "https://github.com/"`git config remote.${1-origin}.url` | sed -e 's#^.*@#https://#' -e 's#.git$##' -e 's#:#/#2'" | |
# Show the url to HEAD commit on GitHub | |
url-last-commit =!"sh -c 'HEAD=`git rev-parse HEAD` && SHA1=`[ "$0" = "sh" ] && echo $HEAD || echo $0` && echo `git url-github`"/commit/"${SHA1i}'" | |
# Open the repository on GitHub | |
# Usage: git open [upstream] (pass the remote name as an optional parameter, e.g., upstream, default is origin) | |
open = "!f() { REPO_URL=$(git config remote.${1-origin}.url | sed -e 's#^.*@#https://#' -e 's#.git$##' -e 's#:#/#2'); git web--browse $REPO_URL; }; f" | |
browse = !"git open" | |
# Keep gh-pages up to date with main branch | |
sync-ghpages = !"git checkout gh-pages && git rebase main && git push origin gh-pages && git checkout main" | |
# Submodule | |
submodule-add = !"f() { ([ $# = 1 ] || [ $# = 2 ]) && git submodule add ${1} ${2} && return 0 || \ | |
echo \"Usage: git submodule-add <URL_to_Git_repo_of_submodule> [<path/to/submodule>]\" >&2 && return 1; }; f" | |
submodule-remove = !"f() { [ $# = 1 ] && git submodule deinit -f -- \"$1\" && rm -rf .git/modules/\"$1\" && git rm -f \"$1\" && return 0 || \ | |
echo \"Usage: git submodule-remove <path/to/submodule>\" >&2 && return 1; }; f" | |
submodule-update = !"git submodule update --init --recursive --remote && git submodule-status" | |
submodule-status = submodule status --recursive | |
### Git Workflow (from https://haacked.com/archive/2014/07/28/github-flow-aliases/) ### | |
# 1 | |
# Make sure the working directory is up-to-date with the origin before creating a new branch with some new work | |
up = !"git pull --rebase --prune $@ && git submodule-update" | |
# 2 | |
# Switch to a branch, creating it if necessary | |
goto = "!f() { git checkout -b \"$1\" 2> /dev/null || git checkout \"$1\"; }; f" | |
# 3 | |
# Commit all changes | |
ca = !"git add . && git commit -m" | |
# 4 | |
save = !"git add -A && git commit -m 'SAVEPOINT'" | |
undo = reset HEAD~ --mixed | |
undo-hard = !"git reset --hard HEAD^" | |
# if you merely need to modify the previous commit | |
amend = commit --amend --all -m | |
# reset work that you "never committed" -> Don't do "git reset HEAD --hard" | |
wipe = "!f() { rev=$(git rev-parse ${1-HEAD}); \ | |
git add -A && \ | |
git commit --allow-empty -qm 'WIPE SAVEPOINT' && \ | |
git reset $rev --hard; }; f" | |
### TOPIC BRANCH ### | |
# use a topic branch for a new feature, or a hotfix patch, or refactoring work, or some spike research, etc. | |
topic-base-branch-name = "!f() { \ | |
git config --get gitalias.topic.base.branch.name || printf '%s\n' main; \ | |
}; f" | |
topic-start = "!f() { \ | |
topic_branch=\"$1\"; \ | |
base_branch=$(git topic-base-branch-name); \ | |
git checkout \"$base_branch\"; git pull; \ | |
git checkout -b \"$topic_branch\" \"$base_branch\"; \ | |
git push --set-upstream origin \"$topic_branch\"; \ | |
}; f" | |
topic-rename = "!f() { \ | |
new_branch=\"$1\"; \ | |
old_branch=$(git branch-name); \ | |
git branch --move \"$old_branch\" \"$new_branch\"; \ | |
git push origin \":$old_branch\" \"$new_branch\"; \ | |
}; f" | |
topic-sync = "!f() { \ | |
topic_branch=$(git branch-name); \ | |
base_branch=$(git topic-base-branch-name); \ | |
if [ \"$topic_branch\" = \"$base_branch\" ]; then \ | |
printf \"You are asking to do git topic-sync,\n\"; \ | |
printf \"but you are not currently on a topic branch;\n\"; \ | |
printf \"you are on the base branch: $base_branch.\n\"; \ | |
printf \"Please checkout the topic branch that you want,\n\"; \ | |
printf \"then retry the git topic-sync command.\n\"; \ | |
else \ | |
git pull --rebase origin \"$topic_branch\"; \ | |
git push; \ | |
fi; \ | |
}; f" | |
topic-merge = "!f() { \ | |
topic_branch=$(git branch-name); \ | |
base_branch=$(git topic-base-branch-name); \ | |
if [ \"$topic_branch\" = \"$base_branch\" ]; then \ | |
printf \"You are asking to do git topic-merge,\n\"; \ | |
printf \"but you are not currently on a topic branch;\n\"; \ | |
printf \"you are on the base branch: $base_branch.\n\"; \ | |
printf \"Please checkout the topic branch that you want,\n\"; \ | |
printf \"then retry the git topic-merge command.\n\"; \ | |
else \ | |
git goto \"$base_branch\"; \ | |
git merge \"$topic_branch\"; \ | |
git ca "Merge \"$topic_branch\" into \"$base_branch\" branch"; \ | |
fi; \ | |
}; f" | |
topic-stop = "!f() { \ | |
topic_branch=$(git branch-name); \ | |
base_branch=$(git topic-base-branch-name); \ | |
if [ \"$topic_branch\" = \"$base_branch\" ]; then \ | |
printf \"You are asking to do git topic-stop,\n\"; \ | |
printf \"but you are not currently on a topic branch;\n\"; \ | |
printf \"you are on the base branch: $base_branch.\n\"; \ | |
printf \"Please checkout the topic branch that you want,\n\"; \ | |
printf \"then retry the git topic-stop command.\n\"; \ | |
else \ | |
git push; \ | |
git checkout \"$base_branch\"; \ | |
git branch --delete \"$topic_branch\"; \ | |
git push origin \":$topic_branch\"; \ | |
fi; \ | |
}; f" | |
# Ignore all untracked files by appending them to .gitignore: | |
ignore = "!git status | grep -P \"^\\t\" | grep -vF .gitignore | sed \"s/^\\t//\" >> .gitignore" | |
# Summary: print a helpful summary of some typical metrics | |
summary = "!f() { \ | |
printf \"Summary of this branch...\n\"; \ | |
printf \"%s\n\" $(git rev-parse --abbrev-ref HEAD); \ | |
printf \"%s first commit timestamp\n\" $(git log --date-order --format=%cI | tail -1); \ | |
printf \"%s latest commit timestamp\n\" $(git log -1 --date-order --format=%cI); \ | |
printf \"%d commit count\n\" $(git rev-list --count HEAD); \ | |
printf \"%d date count\n\" $(git log --format=oneline --format=\"%ad\" --date=format:\"%Y-%m-%d\" | awk '{a[$0]=1}END{for(i in a){n++;} print n}'); \ | |
printf \"%d tag count\n\" $(git tag | wc -l); \ | |
printf \"%d author count\n\" $(git log --format=oneline --format=\"%aE\" | awk '{a[$0]=1}END{for(i in a){n++;} print n}'); \ | |
printf \"%d committer count\n\" $(git log --format=oneline --format=\"%cE\" | awk '{a[$0]=1}END{for(i in a){n++;} print n}'); \ | |
printf \"%d local branch count\n\" $(git branch | grep -v \" -> \" | wc -l); \ | |
printf \"%d remote branch count\n\" $(git branch -r | grep -v \" -> \" | wc -l); \ | |
printf \"\nSummary of this directory...\n\"; \ | |
printf \"%s\n\" $(pwd); \ | |
printf \"%d file count via git ls-files\n\" $(git ls-files | wc -l); \ | |
printf \"%d file count via find command\n\" $(find . | wc -l); \ | |
printf \"%d disk usage\n\" $(du -s | awk '{print $1}'); \ | |
printf \"\nMost-active authors, with commit count and %%...\n\"; git log-of-count-and-email | head -7; \ | |
printf \"\nMost-active dates, with commit count and %%...\n\"; git log-of-count-and-day | head -7; \ | |
printf \"\nMost-active files, with churn count\n\"; git churn | head -7; \ | |
}; f" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment