From a20358865a544135da22e74c1142c3e7f6a4e56b Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Mon, 9 Mar 2026 10:43:09 -0400 Subject: [PATCH] Add custom git commands to dotfiles repo Include git-branch-backup, git-fix-author, git-name-stash, git-name-stash-apply, git-rebase-on, and git-rename-branch in a new bin/ directory. Update install.sh to symlink them into ~/bin and document them in the README. Co-Authored-By: Claude Opus 4.6 --- README.md | 14 +++++++++++ bin/git-branch-backup | 9 +++++++ bin/git-fix-author | 51 ++++++++++++++++++++++++++++++++++++++++ bin/git-name-stash | 3 +++ bin/git-name-stash-apply | 3 +++ bin/git-rebase-on | 4 ++++ bin/git-rename-branch | 4 ++++ install.sh | 9 +++++++ 8 files changed, 97 insertions(+) create mode 100755 bin/git-branch-backup create mode 100755 bin/git-fix-author create mode 100755 bin/git-name-stash create mode 100755 bin/git-name-stash-apply create mode 100755 bin/git-rebase-on create mode 100755 bin/git-rename-branch diff --git a/README.md b/README.md index 06cfaa0..a7a2bd4 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,19 @@ pyenv global 3.13.3 tfenv install latest ``` +## Custom git commands + +The `bin/` directory contains custom git subcommands that are symlinked into `~/bin` by the install script. Make sure `~/bin` is on your PATH (the dotfiles handle this). + +| Command | Description | +|---------|-------------| +| `git branch-backup` | Create a timestamped backup branch of the current branch | +| `git fix-author` | Rewrite commit author/email on historical commits | +| `git name-stash ` | Stash changes with a named message | +| `git name-stash-apply ` | Apply a stash by name | +| `git rebase-on ` | Back up current branch, then pull and rebase onto the given branch | +| `git rename-branch ` | Rename a branch locally and on the remote | + ## What's included | File | Purpose | @@ -159,5 +172,6 @@ tfenv install latest | `gitconfig` | Git aliases, colors, push config | | `gitignore_global` | Global gitignore (.DS_Store, *~) | | `fzf.zsh` | fzf PATH and shell integration | +| `bin/git-*` | Custom git subcommands (see above) | | `Brewfile` | All Homebrew packages and casks | | `install.sh` | Symlink installer script | diff --git a/bin/git-branch-backup b/bin/git-branch-backup new file mode 100755 index 0000000..050f3f9 --- /dev/null +++ b/bin/git-branch-backup @@ -0,0 +1,9 @@ +#!/bin/zsh -f + +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +if [[ $CURRENT_BRANCH == HEAD ]] ; then + CURRENT_BRANCH=$(git rev-parse --short HEAD) # in case detached HEAD +fi +NEW_BRANCH=${CURRENT_BRANCH}-backup-$(date +%Y%m%d-%H%M%S) +git checkout -b $NEW_BRANCH +git checkout - \ No newline at end of file diff --git a/bin/git-fix-author b/bin/git-fix-author new file mode 100755 index 0000000..ecde132 --- /dev/null +++ b/bin/git-fix-author @@ -0,0 +1,51 @@ +#!/bin/bash + +OLD="" +NEW_NAME="" +NEW_EMAIL="" + +ARGS=$(getopt -o o:n:h -l "old:,new:,help" -n $0 -- "$@") + +[ $? -eq 0 ] || exit 1 + +eval set -- "$ARGS" + +while true +do + case "$1" in + -o|--old) + OLD=$2 + shift 2 + ;; + -n|--new) + NEW=$2 + NEW_NAME=$(echo "$NEW" | awk -F'[<>]' '{print $1}' | sed 's/ *$//') + NEW_EMAIL=$(echo "$NEW" | awk -F'[<>]' '{print $2}') + shift 2 + ;; + -h|--help) + echo "usage: git fix-author --old diz@cpan.org --new 'Mike Eldridge ' ref" + exit + ;; + --) + shift + break + ;; + esac +done + +echo "OLD=\"$OLD\"" +echo "NEW_NAME=\"$NEW_NAME\"" +echo "NEW_EMAIL=\"$NEW_EMAIL\"" + +git filter-branch --commit-filter " + if [ \"\$GIT_COMMITTER_EMAIL\" = \"$OLD\" ]; + then + GIT_COMMITTER_NAME=\"$NEW_NAME\"; + GIT_AUTHOR_NAME=\"$NEW_NAME\"; + GIT_COMMITTER_EMAIL=\"$NEW_EMAIL\"; + GIT_AUTHOR_EMAIL=\"$NEW_EMAIL\"; + git commit-tree \"\$@\"; + else + git commit-tree \"\$@\"; + fi" $@ \ No newline at end of file diff --git a/bin/git-name-stash b/bin/git-name-stash new file mode 100755 index 0000000..712752e --- /dev/null +++ b/bin/git-name-stash @@ -0,0 +1,3 @@ +#!/bin/zsh -f + +git stash push -m $1 diff --git a/bin/git-name-stash-apply b/bin/git-name-stash-apply new file mode 100755 index 0000000..c7ef809 --- /dev/null +++ b/bin/git-name-stash-apply @@ -0,0 +1,3 @@ +#!/bin/zsh -f + +git stash apply stash^{/$1} diff --git a/bin/git-rebase-on b/bin/git-rebase-on new file mode 100755 index 0000000..bba1f02 --- /dev/null +++ b/bin/git-rebase-on @@ -0,0 +1,4 @@ +#!/bin/zsh -f + +git branch-backup +git checkout $1 && git pull && git checkout - && git rebase $1 diff --git a/bin/git-rename-branch b/bin/git-rename-branch new file mode 100755 index 0000000..fc2aa38 --- /dev/null +++ b/bin/git-rename-branch @@ -0,0 +1,4 @@ +#!/bin/zsh -f + +git branch-backup +git checkout $1 && git branch -m $2 && git push origin -u $2 && git push origin --delete $1 diff --git a/install.sh b/install.sh index c703a0b..9b37880 100755 --- a/install.sh +++ b/install.sh @@ -55,6 +55,15 @@ link_file "gitconfig" ".gitconfig" link_file "gitignore_global" ".gitignore_global" link_file "fzf.zsh" ".fzf.zsh" +# Symlink custom git commands into ~/bin +echo "" +echo "=== Custom git commands ===" +mkdir -p "$HOME/bin" +for cmd in "$DOTFILES_DIR"/bin/git-*; do + name="$(basename "$cmd")" + link_file "bin/$name" "bin/$name" +done + echo "" echo "=== Symlinks complete ===" echo ""