An opinionated collection of cross-platform Git utilities that provide interactive ways to undo commits, stash changes, and clean up branches, etc. while preserving your work with a test suite to ensure safety.
The goal of these utilities is to make getting to a clean working state in any branch safe and easy, while still allowing you to be as creative and productive as you need to be.
USE AT YOUR OWN RISK! (I use them every day, but of course, YMMV).
While these tools are tested daily, always back up your work and use caution when modifying git history.
Built with the assistance of Claude Code. CLAUDE.md
is included.
I am on a Mac Sequoia 15.5 (Silicon) using Warp terminal. I've tried to keep these functions as portable as possible. If you have an issue, run the test script in debug mode and post an Issue on GitHub in this repository.
PRs welcome!
This toolkit provides eight essential Git utilities:
Function | Purpose | Key Benefits |
---|---|---|
git-undo |
Safely undo the last commit while preserving changes in a stash | Interactive preview, metadata preservation, safe rollback |
git-redo |
Restore previously undone commits from undo stashes | Smart stash detection, interactive selection, conflict-safe |
git-stash |
Stash all changes (including untracked files) to get a clean working directory | Complete file coverage, enhanced file status display, guaranteed clean state |
git-clean-branches |
Clean up merged and orphaned branches with detailed previews | Branch protection, detailed reporting, selective cleanup |
git-squash |
Squash all commits in current branch into the oldest commit | Interactive commit message editing, preserves authorship, uses current date |
git-status |
Show count of commits and untracked files, what branch any feature branch forked from | Smart base detection, enhanced file status display, develop preference |
git-show-branches |
Display all local branches with their remote tracking status and sync state | Visual branch overview, ahead/behind status, remote tracking info |
git-show-stashes |
Display all stashes with name and smart age formatting | Smart age display (minutes/hours/days), color-coded by age, verbose mode support |
git-clean-stashes |
Clean up old stashes from your repository with age-based filtering | Age-based filtering, preview before deletion, batch operations |
All functions follow the same safe pattern: show what will happen, ask for confirmation, then execute with clear feedback.
All you need is the main script, git-toolkit.sh
. Just add it to your shell and restart your terminal. The steps below cover the basics, but if you run into any issues or your setup is a bit different, a quick search for your operating system’s specifics should point you in the right direction.
Copy and paste instructions for any zsh
shell. zsh
has been the default shell on macOS since 2019 Catalina.
-
Download the script to your user folder
curl -L -o ~/git-toolkit.sh https://raw.githubusercontent.com/babul/git-toolkit/main/git-toolkit.sh
-
Load the script into your shell:
source ~/git-toolkit.sh
-
Add the script to your shell configuration file to load it automatically on startup
echo 'source ~/git-toolkit.sh' >> ~/.zshrc
-
To update script just repeat steps 1 & 2
Here is copy/paste instructions for any bash
shell. macOS versions prior to Catalina use bash
as the default shell, but you will need Bash 4.0+.
-
Download the script to your user folder
curl -L -o ~/git-toolkit.sh https://raw.githubusercontent.com/babul/git-toolkit/main/git-toolkit.sh
-
Load the script into your shell:
source ~/git-toolkit.sh
-
Add the script to your shell configuration file to load it automatically on startup
echo 'source ~/git-toolkit.sh' >> ~/.bashrc
-
To update script just repeat steps 1 & 2
Safely undo the last commit while preserving all changes.
git-undo
Features:
- Shows complete commit details before undoing
- Stashes changes with metadata for easy recovery
- Protects against undoing initial commits
- Requires clean working directory
Note:
- This only works for commits that you have not pushed up!
Stash all changes, including untracked files to get a clean branch.
git-stash [message]
Options:
message
Optional custom message to include in the stash name (space-separated words will be formatted)
Features:
- Handles modified, staged, and untracked files
- Shows preview of what will be stashed with enhanced file status display
- Enhanced file status display: Shows deleted, renamed, modified, and new files with color coding
- Visual file states: Modified/renamed files in green, deleted/untracked files in red
- Uses
--include-untracked
for complete cleanup (excludes ignored files) - Branch-named and timestamped stash messages for easy identification
- Custom message support with automatic formatting
Restore previously undone commits by selecting from available undo stashes.
git-redo
Features:
- Lists all available previous git-undo operations with details
- Shows commit message, timestamp, and original hash
- Interactive selection of which undo to restore
- Applies changes to working directory (not as commit)
- Preserves stash for potential future use
Clean up merged branches and branches closed or deleted from remote.
git-clean-branches
Features:
- Identifies merged and orphaned branches
- Shows branch details and last commit info
- Protects current branch and main/master/develop
- Force-deletes branches gone from remote
- Provides guidance for unmerged branches
Squash all commits in the current branch into the oldest commit.
git-squash
Features:
- Automatically finds base branch (main/master/develop)
- Shows detailed preview of commits to be squashed
- Opens editor to modify the commit message
- Preserves original author, uses current date/time
- Protects main/master/develop branches from being squashed
- Safe rollback if editor is canceled or fails
Show what branch any feature branch forked from, or show pending commits for main/master/develop branches.
git-status [options] [branch-name]
Options:
-v
Show commits since fork (feature branches) or pending commits (main/master/develop)-vv
Show full commits since fork (feature branches) or pending commits (main/master/develop)--debug
Show detailed diagnostic information for troubleshooting
Features:
- Feature branches: Automatically detects fork point from main/master/develop
- Main branches: Shows pending commits since last push to remote
- No remote: Shows total commit count when no remote tracking branch exists
- Prefers develop over main when both are equidistant for feature branches
- Shows commit history with verbose options for both branch types
- Enhanced file status display: Shows deleted, renamed, modified, and new files with color coding
- Visual file states: Modified/renamed files in green, deleted/untracked files in red
- Suppresses debug output for clean results
Display all local branches with their remote tracking status and sync state.
git-show-branches [options]
Options:
-v
Show last commit message for each branch-vv
Show full last commit details for each branch--debug
Show debug information
Features:
- Visual branch overview: Shows all local branches in a formatted table
- Remote tracking status: Displays remote tracking branch for each local branch
- Sync status: Shows if branches are ahead/behind their remote counterparts
- Color coding: Yellow for branch names, cyan for remotes, green/yellow/red for status
- Column alignment: Auto-adjusts column widths for optimal readability
- Commit info: Shows last commit details with verbose options
Display all stashes with their names and age in a clean, formatted table.
git-show-stashes [options]
Options:
-v
Show number of files changed in each stash-vv
Show full diff stat for each stash--debug
Show debug information
Features:
- Stash visualization: Shows all stashes with message and age (no date/time for cleaner output)
- Smart age display:
- Recent stashes: "< 1 minute", "5 minutes", "45 minutes"
- Hours: "1 hour", "2 hours 30 min", "23 hours 59 min"
- Days: "1 day", "30 days", "90 days"
- Color coding by age: Green (<30 days), yellow (30-60 days), red (>60 days)
- Column alignment: Auto-adjusts column widths for optimal readability
- Verbose modes: Shows files changed count or full diff statistics
- Chronological order: Most recent stashes appear first
Clean up old stashes from your repository based on age.
git-clean-stashes [options]
Options:
--age=days
Only show stashes older than X days (default: 60)--debug
Show debug information including timestamps and stash counts
Features:
- Age-based filtering: Default 60 days, customizable with --age parameter
- Safe deletion: Shows preview of all stashes to be deleted before confirmation
- Batch operations: Deletes multiple stashes in correct order to avoid reference issues
- Detailed preview: Shows stash age, creation date, and message
- Success reporting: Reports number of stashes successfully deleted
Both git-status
and git-stash
now provide enhanced file status visualization with color coding:
- Modified files: Show as
modified: filename.js
in green - Renamed files: Show as
renamed: old-name.js -> new-name.js
in green - Deleted files: Show as
deleted: filename.js
in red - New files: Show as
new file: filename.js
in green when staged - Untracked files: Listed by filename in red
- Green: Positive changes (modified, renamed, new files)
- Red: Items needing attention (deleted, untracked files)
Files are grouped into sections matching Git's standard output:
- Changes to be committed (staged files)
- Changes not staged for commit (unstaged modifications/deletions)
- Untracked files (files not in Git)
This enhanced display makes it easier to understand exactly what changes are present in your working directory and what will be affected by Git operations.
$ git-undo
About to undo commit:
Hash: a1b2c3d
Message: Add new feature
Author: John Doe <[email protected]>
Date: 2024-01-15 10:30:00
Proceed with undo? (y/N): y
✓ Commit undone and changes stashed
$ git-redo
Available undo operations to redo:
1. Add new feature
Undone: 2024-01-15 10:30:00
Original hash: a1b2c3d
Stash: stash@{0}
2. Fix bug in parser
Undone: 2024-01-15 09:15:00
Original hash: e4f5g6h
Stash: stash@{1}
Enter the number of the undo to redo (or 'q' to quit): 1
About to redo (restore) commit:
Commit: Add new feature
Stash: stash@{0}
Proceed with redo? (y/N): y
✓ Redo completed successfully!
The changes have been restored to your working directory.
You can now:
- Review the changes with: git diff
- Commit them again with: git commit
- Drop the stash with: git stash drop stash@{0}
$ git-stash
Stashing all changes (including untracked files)...
Files to be stashed:
Changes to be committed:
renamed: old-config.js -> config.js
new file: utils.js
Changes not staged for commit:
modified: main.js
deleted: legacy.js
Untracked files:
temp.txt
Proceed with stashing all changes? (y/N): y
✓ All changes stashed successfully!
Working directory is now clean.
Changes saved in stash: stash@{0} ("stash feature-branch")
To restore: git stash apply stash@{0}
$ git-clean-branches
About to delete branches:
Branch: feature-1 (merged)
Last commit: a1b2c3d Add feature 1
Branch: feature-2 (gone from remote)
Last commit: d4e5f6g Update feature 2
Proceed with branch cleanup? (y/N): y
✓ Deleted branch: feature-1
✓ Force deleted gone branch: feature-2
✓ Branch cleanup completed
$ git-squash
About to squash commits:
Branch: feature-branch
Base: main
Commits to squash: 3
Into commit: a1b2c3d (Add initial feature)
Author: John Doe <[email protected]>
Date: Current date/time
Commits being squashed:
1. a1b2c3d Add initial feature
2. e4f5g6h Update feature implementation
3. h7i8j9k Fix feature bugs
Proceed with squash? (y/N): y
Opening editor to edit commit message...
✓ Successfully squashed 3 commits
✓ Commit message updated
Squashed commit:
k9l0m1n Add initial feature with updates and fixes
# Feature branch - basic usage
$ git-status
The feature/sentry-posthog branch forked from develop at commit 8ac50fd2
# Feature branch - specific branch
$ git-status bugfix/login-issue
The bugfix/login-issue branch forked from main at commit 3f7a9e12
# Feature branch - show commits since fork (one line each)
$ git-status -v feature/analytics
The feature/analytics branch forked from develop at commit 8ac50fd2
Uncommitted changes:
Changes to be committed:
renamed: old-config.js -> config.js
new file: analytics.js
Changes not staged for commit:
modified: main.js
deleted: legacy.js
Untracked files:
temp.log
Commits since fork:
a1b2c3d Add event tracking
e4f5g6h Configure analytics dashboard
h7i8j9k Fix tracking bugs
# Main branch - show pending commits
$ git-status main
The main branch has 3 pending commit(s) since last push
# Main branch - show pending commits with details
$ git-status -v main
The main branch has 3 pending commit(s) since last push
Pending commits:
a1b2c3d Fix user authentication bug
e4f5g6h Update documentation
h7i8j9k Add new feature endpoint
# Main branch - no remote tracking branch
$ git-status main
The main branch has 15 total commit(s) (no remote tracking branch)
# Main branch - show all commits when no remote
$ git-status -v main
The main branch has 15 total commit(s) (no remote tracking branch)
All commits:
a1b2c3d Latest changes
e4f5g6h Previous commit
h7i8j9k Initial commit
# Feature branch - full verbose mode
$ git-status -vv feature/new-ui
The feature/new-ui branch forked from develop at commit 8ac50fd2
Commits since fork:
commit a1b2c3d4e5f6789...
Author: Jane Doe <[email protected]>
Date: Tue Jan 16 09:30:00 2024 -0500
Add new UI components
- Implement modern button styles
- Add responsive navigation
- Update color scheme
commit e4f5g6h7i8j9012...
Author: Jane Doe <[email protected]>
Date: Tue Jan 16 11:15:00 2024 -0500
Refactor layout system
- Use CSS Grid for main layout
- Improve mobile responsiveness
# Basic usage - show all branches
$ git-show-branches
main origin/main Status: 5 ahead
feature/test-branch origin/feature/test-branch Status: up to date
hotfix/urgent-fix (local only) Status: 3 local commit(s)
develop origin/develop Status: 2 ahead, 1 behind
# Verbose mode - show last commit
$ git-show-branches -v
main origin/main Status: 5 ahead
└─ abc123d Add new feature
feature/test-branch origin/feature/test-branch Status: up to date
└─ def456e Update tests
hotfix/urgent-fix (local only) Status: 3 local commit(s)
└─ ghi789f Fix critical bug
develop origin/develop Status: 2 ahead, 1 behind
└─ jkl012g Merge pull request #42
# Full verbose mode - show full commit details
$ git-show-branches -vv
main origin/main Status: 5 ahead
└─ abc123d Add new feature
Added support for new authentication method
- Implemented OAuth2 flow
- Updated user model
feature/test-branch origin/feature/test-branch Status: up to date
└─ def456e Update tests
Improved test coverage for auth module
# Basic usage - show all stashes
$ git-show-stashes
Stashes:
On main: WIP authentication fixes 5 minutes
On feature/ui: Save work before switching 2 hours 30 min
On develop: Temp save for meeting 18 days
On main: Experimental feature 44 days
On hotfix/bug-123: Quick save 84 days
# Verbose mode - show files changed
$ git-show-stashes -v
Stashes:
On main: WIP authentication fixes 5 minutes
└─ 3 file(s) changed
On feature/ui: Save work before switching 2 hours 30 min
└─ 7 file(s) changed
On develop: Temp save for meeting 18 days
└─ 1 file(s) changed
# Full verbose mode - show diff stat
$ git-show-stashes -vv
Stashes:
On main: WIP authentication fixes 5 minutes
└─ 3 files changed, 45 insertions(+), 12 deletions(-)
On feature/ui: Save work before switching 2 hours 30 min
└─ 7 files changed, 234 insertions(+), 89 deletions(-)
# Default usage - clean stashes older than 60 days
$ git-clean-stashes
Found 3 stash(es) older than 60 days:
Stash: stash@{1}
Age: 174 days (created: 2024-05-30 12:38:32)
Message: On main: Fork autostash May 30, 2024 at 12:38 PM
Stash: stash@{2}
Age: 397 days (created: 2023-12-31 09:15:42)
Message: WIP on develop: b8ba715 use prince.aliada.io instead of app.posthog.com
Stash: stash@{3}
Age: 418 days (created: 2023-12-10 14:22:15)
Message: On main: Checkout autostash Dec 10, 2023 at 2:22 PM
Proceed with deleting these 3 old stash(es)? (y/N): y
✓ Deleted stash: stash@{3}
✓ Deleted stash: stash@{2}
✓ Deleted stash: stash@{1}
✓ Successfully deleted 3 stash(es)
# Custom age - clean stashes older than 30 days
$ git-clean-stashes --age=30
Found 5 stash(es) older than 30 days:
...
# Debug mode
$ git-clean-stashes --debug --age=90
=== DEBUG MODE ===
Age threshold: 90 days
Current timestamp: 1735689600
Cutoff timestamp: 1727913600
Total stashes: 12
==================
Found 2 stash(es) older than 90 days:
...
# Not in a git repository
$ git-undo
✗ Error: Not a git repository
# Uncommitted changes present
$ git-undo
✗ Error: Working directory is not clean. Please commit or stash your changes first.
# No commits to undo
$ git-undo
✗ Error: Repository has no commits
# git-status invalid option
$ git-status -x
✗ Error: Unknown option '-x'
Usage: git-status [-v|-vv] [branch-name]
-v Show commits since fork (feature branches) or pending commits (main/master/develop)
-vv Show full commits since fork (feature branches) or pending commits (main/master/develop)
# git-clean-stashes invalid age
$ git-clean-stashes --age=abc
✗ Error: Invalid age value 'abc'. Must be a positive integer.
All functions follow a consistent safety pattern:
- Validation - Check git repository state and prerequisites
- Preview - Show exactly what will happen
- Confirmation - Interactive y/N prompt
- Execution - Perform operation with clear feedback
- Success/Error reporting - Visual indicators (✓/✗) for all outcomes
- POSIX-compliant shell syntax - works on bash, zsh, and other POSIX shells
- Portable commands - avoids system-specific features
- macOS, Linux, and Unix support - tested across platforms
- Shared utility functions - DRY principle with common validation logic
- Comprehensive error handling - graceful failure with helpful messages
- Consistent user interface - same patterns across all functions
- Modular design - each function is independent and focused
Run the comprehensive test suite:
./test-git-toolkit.sh
Test with specific shells for maximum compatibility:
bash ./test-git-toolkit.sh # Test under bash
sh ./test-git-toolkit.sh # Test under POSIX sh
zsh ./test-git-toolkit.sh # Test under zsh
Run tests in debug mode for troubleshooting:
./test-git-toolkit.sh --debug
Test coverage includes:
- All nine functions (
git-undo
,git-redo
,git-stash
,git-clean-branches
,git-squash
,git-status
,git-show-branches
,git-show-stashes
,git-clean-stashes
) - Error conditions and edge cases
- User interaction scenarios (confirmation, cancellation)
- Cross-platform compatibility validation
- Special character handling in stash messages
- Verbose mode operations (-v, -vv)
- 89 total tests with colored pass/fail output
Test breakdown by category:
- Cross-platform compatibility: 11 assertions
- git_undo function: 11 assertions
- git_stash function: 10 assertions
- git_clean_branches function: 10 assertions
- git_redo function: 9 assertions
- git_squash function: 8 assertions
- git_status function: 14 assertions
- git_show_branches function: 6 assertions
- git_clean_stashes function: 8 assertions
- git_show_stashes function: 9 assertions
[TEST] Starting git-toolkit.sh test suite...
==========================================
TESTING: Cross-platform compatibility
==========================================
[TEST] Cross-platform: Shell feature compatibility
Test confirmation function (simulate 'n' response)Test prompt (y/N):
Test confirmation function (simulate 'y' response)
Test prompt (y/N):
[PASS] All cross-platform utility functions work correctly (6/6)
[TEST] _git_format_timestamp: Edge case testing
Testing normal timestamp generation...
Testing fallback when DATE_FORMAT would be empty...
Fallback logic works: ''2025-07-03 23:20:49''
Testing actual function consistency...
Function consistency works: '2025-07-03 23:20:49'
Testing timestamp consistency...
[PASS] _git_format_timestamp works correctly in all edge cases (4/4)
[TEST] Cross-platform: Shell syntax compatibility
[PASS] Script syntax is valid in bash
==========================================
TESTING: git_undo function
==========================================
[TEST] git_undo: Not in git repository
[PASS] Correctly detected not in git repository
[TEST] git_undo: Initial commit protection
[PASS] Correctly prevented undoing initial commit
[TEST] git_undo: Dirty working directory
[PASS] Correctly detected dirty working directory
[TEST] git_undo: Cancel undo operation
[PASS] Cancel operation works correctly
[TEST] git_undo: Normal undo operation
[PASS] Commit was successfully undone
[PASS] Stash was created with correct message
[PASS] Metadata was stored in stash with correct content
[TEST] git_undo: Special characters in commit
stash@{0}: On main: undo main [sc-123] fix: handle special chars (test) & more [brackets]
[PASS] Handled special characters in commit message
[TEST] git_undo: Multiple undos in sequence
[PASS] Multiple undos work correctly
[TEST] git_undo: Comprehensive metadata preservation
[PASS] Comprehensive metadata preservation works
==========================================
TESTING: git_stash function
==========================================
[TEST] git_stash: Not in git repository
[PASS] git_stash correctly detected not in git repository
[TEST] git_stash: Repository with no commits
[PASS] git_stash correctly detected repository with no commits
[TEST] git_stash: Clean working directory
[PASS] git_stash correctly detected clean working directory
[TEST] git_stash: Cancel stash operation
[PASS] git_stash cancel operation works correctly
[TEST] git_stash: Normal stash with modified files
[PASS] git_stash cleaned working directory
[PASS] git_stash created stash with correct message
[TEST] git_stash: Stash with untracked files
[PASS] git_stash handled both modified and untracked files
[PASS] git_stash included untracked files in stash
[TEST] git_stash: Stash with staged files
[PASS] git_stash handled staged files correctly
[TEST] git_stash: Complex scenario with all file types
[PASS] git_stash completely cleaned complex working directory
[PASS] git_stash preserved all file types correctly
==========================================
TESTING: git_clean_branches function
==========================================
[TEST] git_clean_branches: Not in git repository
[PASS] git_clean_branches correctly detected not in git repository
[TEST] git_clean_branches: Repository with no commits
[PASS] git_clean_branches correctly detected repository with no commits
[TEST] git_clean_branches: No branches to clean
[PASS] git_clean_branches correctly detected no branches to clean
[TEST] git_clean_branches: Cancel operation
[PASS] git_clean_branches cancel operation works correctly
[TEST] git_clean_branches: Clean merged branches
[PASS] git_clean_branches successfully cleaned merged branches
[PASS] git_clean_branches preserved main branch
[TEST] git_clean_branches: Protect current branch
[PASS] git_clean_branches protected current branch
[TEST] git_clean_branches: Handle unmerged branches
[PASS] git_clean_branches protected unmerged branch
[PASS] git_clean_branches deleted merged branch
[PASS] git_clean_branches properly handled unmerged branch
==========================================
TESTING: git_redo function
==========================================
[TEST] git_redo: Not in git repository
[PASS] git_redo correctly detected not in git repository
[TEST] git_redo: Repository with no commits
[PASS] git_redo correctly detected repository with no commits
[TEST] git_redo: No undo stashes available
[PASS] git_redo correctly detected no undo stashes
[TEST] git_redo: Dirty working directory
[PASS] git_redo correctly detected dirty working directory
[TEST] git_redo: Cancel redo operation
[PASS] git_redo cancel at selection works correctly
[PASS] git_redo cancel at confirmation works correctly
[TEST] git_redo: Successful redo operation
[PASS] File was removed after undo
[PASS] git_redo successfully restored changes
[PASS] git_redo left changes in working directory
==========================================
TESTING: git_squash function
==========================================
[TEST] git_squash: Not in git repository
[PASS] git_squash correctly detected not in git repository
[TEST] git_squash: Repository with no commits
[PASS] git_squash correctly detected repository with no commits
[TEST] git_squash: Dirty working directory
[PASS] git_squash correctly detected dirty working directory
[TEST] git_squash: On protected branch
[PASS] git_squash correctly prevented squashing on main branch
[TEST] git_squash: Only one commit on branch
[PASS] git_squash correctly detected only one commit
[TEST] git_squash: Cancel squash operation
[PASS] git_squash cancel operation works correctly
[TEST] git_squash: Successful squash operation
[PASS] git_squash function defined and preview works (interactive test skipped)
[TEST] git_squash: No base branch found
[PASS] git_squash correctly detected no base branch
==========================================
TESTING: git_status function
==========================================
[TEST] git_status: Not in git repository
[PASS] git_status correctly detected not in git repository
[TEST] git_status: Repository with no commits
[PASS] git_status correctly detected repository with no commits
[TEST] git_status: On default branch (show pending commits)
[PASS] git_status correctly showed commit count for main branch without remote
[TEST] git_status: Default branch verbose modes
[PASS] git_status -v correctly showed commits for main branch
[PASS] git_status -vv correctly showed full commits for main branch
[TEST] git_status: Nonexistent branch
[PASS] git_status correctly detected nonexistent branch
[TEST] git_status: Basic functionality
[PASS] git_status correctly identified branch fork point
[TEST] git_status: With specific branch parameter
[PASS] git_status correctly identified specific branch fork point
[TEST] git_status: Verbose mode (-v)
[PASS] git_status -v correctly showed commits since fork
[TEST] git_status: Full verbose mode (-vv)
[PASS] git_status -vv correctly showed full commit details
[TEST] git_status: Invalid option
[PASS] git_status correctly handled invalid option
[TEST] git_status: Develop branch preference
[PASS] git_status correctly preferred develop over main
[TEST] git_status: Show deleted files with -v
[PASS] git_status -v correctly showed deleted and modified files
[TEST] git_status: Show renamed files with -v
[PASS] git_status -v correctly showed renamed file
[TEST] git_stash: Show deleted and renamed files
[PASS] git_stash correctly showed deleted, renamed, and new files
==========================================
TESTING: git_show_branches function
==========================================
[TEST] git_show_branches: Not in git repository
[PASS] git_show_branches correctly detected not in git repository
[TEST] git_show_branches: Repository with no commits
[PASS] git_show_branches correctly detected repository with no commits
[TEST] git_show_branches: Single branch (local only)
[PASS] git_show_branches correctly showed single local branch
[TEST] git_show_branches: Multiple branches with different statuses
[PASS] git_show_branches correctly showed multiple branches
[TEST] git_show_branches: Verbose mode
[PASS] git_show_branches -v correctly showed commit information
[TEST] git_show_branches: Invalid option
[PASS] git_show_branches correctly handled invalid option
==========================================
TESTING: git_clean_stashes function
==========================================
[TEST] git_clean_stashes: Not in git repository
[PASS] git_clean_stashes correctly detected not in git repository
[TEST] git_clean_stashes: Repository with no commits
[PASS] git_clean_stashes correctly detects repository with no commits
[TEST] git_clean_stashes: No stashes available
[PASS] git_clean_stashes correctly reports no stashes
[TEST] git_clean_stashes: Invalid age parameter
[PASS] git_clean_stashes correctly rejects invalid age
[TEST] git_clean_stashes: Cancel cleanup operation
Saved working directory and index state On main: old test stash
[PASS] git_clean_stashes correctly handles user cancellation
[TEST] git_clean_stashes: No old stashes (all recent)
Saved working directory and index state On main: recent test stash
[PASS] git_clean_stashes correctly reports no old stashes
[TEST] git_clean_stashes: Successfully clean old stashes
Saved working directory and index state On main: test stash to clean
[PASS] git_clean_stashes successfully cleaned old stashes
[TEST] git_clean_stashes: Debug mode output
Saved working directory and index state On main: debug test stash
[PASS] git_clean_stashes debug mode works correctly
==========================================
TESTING: git_show_stashes function
==========================================
[TEST] git_show_stashes: Not in git repository
[PASS] git_show_stashes correctly detected not in git repository
[TEST] git_show_stashes: Repository with no commits
[PASS] git_show_stashes correctly detected repository with no commits
[TEST] git_show_stashes: Empty stash list
[PASS] git_show_stashes correctly handles empty stash list
[TEST] git_show_stashes: Basic stash display
Saved working directory and index state On main: First stash
Saved working directory and index state On main: Second stash
[PASS] git_show_stashes displays basic stash list correctly
[TEST] git_show_stashes: Verbose mode (-v)
Saved working directory and index state On main: Test stash with multiple files
[PASS] git_show_stashes -v shows file count
[TEST] git_show_stashes: Full verbose mode (-vv)
Saved working directory and index state On main: Test stash for full verbose
[PASS] git_show_stashes -vv shows full diff stats
[TEST] git_show_stashes: Invalid options
[PASS] git_show_stashes correctly rejects invalid options
[TEST] git_show_stashes: Stashes with special characters
Saved working directory and index state On main: [special] chars & (test) #1
Saved working directory and index state On main: Test with 'quotes' and "double quotes"
[PASS] git_show_stashes handles special characters correctly
[TEST] git_show_stashes: Debug mode
Saved working directory and index state On main: Debug test stash
[PASS] git_show_stashes debug mode works correctly
===============================================
Test Results: 89 passed, 0 failed
===============================================
All tests passed!
The test suite provides comprehensive coverage across 89 tests organized into 9 categories:
Category | Tests | Coverage |
---|---|---|
Cross-platform compatibility | 3 | Shell feature validation, timestamp edge cases, syntax compatibility |
git_undo function | 9 | Error conditions, user interactions, metadata preservation |
git_stash function | 11 | File type handling, clean state verification, branch naming, deleted/renamed files |
git_clean_branches function | 11 | Branch detection, protection logic, deletion safety |
git_redo function | 9 | Stash restoration, user selection, working directory checks |
git_squash function | 8 | Commit consolidation, editor integration, branch protection |
git_status function | 15 | Fork detection, verbose modes, main branch pending commits, branch validation, deleted/renamed files |
git_show_branches function | 6 | Branch listing, remote tracking status, verbose modes, error handling |
git_clean_stashes function | 8 | Age filtering, batch deletion, user cancellation, debug mode |
Test Types:
- Error condition tests (24 tests): Repository validation, commit existence, permission checks
- Safety mechanism tests (18 tests): Working directory protection, branch safeguards, user confirmation
- Core functionality tests (28 tests): Primary operations, data integrity, expected behaviors, timestamp edge cases, main branch pending commits, deleted/renamed file detection
- User interaction tests (10 tests): Cancellation handling, input validation, confirmation prompts
Key Test Scenarios:
- Repository state validation: Tests all functions in non-git directories and empty repositories
- User cancellation: Verifies all functions handle user cancellation gracefully
- Special cases: Complex commit messages, multiple file types, edge conditions
- Timestamp edge cases: Empty DATE_FORMAT handling, fallback behavior validation
- Cross-platform compatibility: POSIX compliance and portable shell features
- Shell: Bash 4.0+ or Zsh (POSIX-compliant)
- Git: 2.0 or higher
- OS: macOS, Linux, Unix, or WSL
All git-toolkit functions support a --debug
flag for troubleshooting:
git-undo --debug
git-redo --debug
git-stash --debug
git-clean-branches --debug
git-squash --debug
git-status --debug
git-clean-stashes --debug
Debug mode provides detailed diagnostic information including:
- Branch detection and pattern matching
- Variable values and code path selection
- Detailed execution flow for troubleshooting
- Metadata preservation: Full commit details stored in stash
- Special character handling: Supports complex commit messages
- Initial commit protection: Prevents undoing first commit
- Timestamped stashes: Easy identification and recovery
- Smart stash detection: Only shows undo-created stashes
- Rich metadata display: Shows original hash, timestamp, and commit message
- Interactive selection: Choose which specific undo to restore
- Non-destructive: Applies to working directory, preserves stash
- Conflict handling: Graceful handling of merge conflicts
- Complete coverage: Modified, staged, and untracked files
- Preview functionality: See exactly what will be stashed
- Clean state guarantee: Working directory guaranteed clean after stashing
- Smart detection: Identifies both merged and remote-gone branches
- Branch protection: Safeguards current, main, master, and develop branches
- Detailed reporting: Shows branch type and last commit info
- Selective deletion: Different handling for merged vs. unmerged branches
- Automatic base detection: Finds main/master/develop branch automatically
- Interactive commit editing: Opens editor for customizing squashed commit message
- Authorship preservation: Maintains original author, uses current date/time
- Smart commit preview: Shows detailed list of commits being squashed
- Safe operation: Protects against squashing main branches, provides rollback on failure
- Cross-platform editor support: Works with any configured git editor
- Dual functionality: Works on both feature branches and main/master/develop branches
- Feature branches: Smart base detection, automatically finds fork point from main/master/develop
- Main branches: Shows pending commits since last push, or total commits if no remote
- Develop preference: Choose develop over main when distances are equal for feature branches
- Verbose modes: Optional one-line (-v) or full commit (-vv) history for both branch types
- Clean output: Suppresses debug output for professional results
- Flexible usage: Works with current branch or specified branch name
- Debug mode: Detailed diagnostic information available with
--debug
flag
- Age-based filtering: Customizable age threshold for stash cleanup (default 60 days)
- Batch operations: Efficiently deletes multiple stashes in correct order
- Reference management: Handles stash reference shifting during deletion
- Preview functionality: Shows detailed information before deletion
- Progress reporting: Real-time feedback during deletion process
- Debug information: Timestamps, counts, and processing details
- One commit at a time (by design for safety)
- Cannot undo initial commit (git limitation)
- Only works with undo-created stashes (by design)
- Requires clean working directory (safety feature)
- Restores to working directory, not as commit (intentional)
- Requires commits to exist (cannot stash in empty repository)
- Requires commits to exist for branch comparison
- Cannot delete current branch (safety feature)
- Only works on feature branches (protects main/master/develop)
- Requires at least two (2) commits on branch to squash
- Needs base branch (main/master/develop) to exist for comparison
- Editor cancellation or empty message cancels the operation
- Feature branches: Requires base branches (main/master/develop) to exist for comparison
- Feature branches: May not detect correct base if branch history is complex or rebased
- Main branches: Pending commit detection requires remote tracking branch for accurate count
- Verbose modes (-v/-vv) require commits to exist since fork point or in branch history
- Requires commits to exist (cannot work in empty repository)
- Cannot recover stashes after deletion (permanent operation)
- Age calculation based on stash creation timestamp
- No filtering by stash content or branch name (age-based only)
Contributions are welcome! Please:
- Follow the established code patterns
- Add tests for new functionality
- Ensure cross-platform compatibility
- Update documentation
A while ago, I created a simple git-undo
bash function to solve one of my recurring pain points. Thanks to Claude Code, I was able to expand that into a full set of “missing” Git commands I’d always wanted.
With the productivity boost from working with Claude Code, these utilities have become even more valuable to me. I hope you find them useful too!
If you do, feel free to reach out on X at @tmgbabul.
Wishing you safety, security, and good health!
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to https://unlicense.org