Welcome to Maiao! This guide will help you set up and start using stacked pull requests in your GitHub workflow.
Before installing Maiao, ensure you have:
- Git installed (version 2.0+)
- GitHub account with repository access
- GitHub Personal Access Token with
reposcope (create one here)
brew tap adevinta/maiao https://github.com/adevinta/maiao.git
brew install maiao- Visit the releases page
- Download the appropriate binary for your system
- Install it:
# Download and install
mv <downloadsDir>/git-review-`uname -s`-`uname -m` /usr/local/bin/git-review
chmod +x /usr/local/bin/git-review
# macOS only: Remove quarantine flag
xattr -d com.apple.quarantine /usr/local/bin/git-review- Visit the releases page
- Download
git-review-windows-<arch>(usuallyamd64) - Add to your PATH
go build -o /usr/local/bin/git-review ./cmd/maiaoCreate a ~/.netrc file with your GitHub credentials:
machine github.com
login your.username@example.com
password <your-personal-access-token>
For GitHub Enterprise:
machine github.company.example.com
login firstname.lastname@company.example.com
password <your-token>
Use your OS keychain (macOS Keychain, pass, etc.) instead of .netrc:
export MAIAO_EXPERIMENTAL_CREDENTIALS=true
git reviewSupported keychains: 99designs/keyring
In your repository, install the Gerrit commit-msg hook:
cd /path/to/your/repo
git review installThis installs a Git hook that automatically adds a unique Change-Id to every commit message.
Check that the hook is installed:
ls -la .git/hooks/commit-msg
# Should show the commit-msg hook fileMaiao aims to solve the classic code review problem:
Philosophy: Create small, self-contained commits. Each commit becomes a reviewable PR, stacked on the previous one.
Benefits:
- ✅ Faster reviews (smaller changesets)
- ✅ Better feedback (focused on one change)
- ✅ Cleaner history (atomic commits)
- ✅ Easier debugging (bisectable changes)
Let's say you're building a new authentication feature requiring changes to 2 files and 2 test files.
Commit 1: Database Schema
# 1. Make changes to database schema
vim db/schema.sql
# 2. Write tests
vim tests/db_test.go
# 3. Ensure tests pass
go test ./tests/db_test.go
# 4. Commit atomically
git add db/schema.sql tests/db_test.go
git commit -m "Add user authentication schema"
# Hook automatically adds: Change-Id: I111abc...Commit 2: Authentication Logic
# 5. Implement auth logic
vim auth/handler.go
# 6. Write tests
vim tests/auth_test.go
# 7. Ensure tests pass
go test ./tests/auth_test.go
# 8. Commit atomically
git add auth/handler.go tests/auth_test.go
git commit -m "Add JWT authentication handler"
# Hook automatically adds: Change-Id: I222def...Create Stacked PRs
# 9. Submit for review
git reviewResult:
Created PR https://github.com/org/repo/pull/101
Title: Add user authentication schema
Base: main
Head: maiao.I111abc...
Created PR https://github.com/org/repo/pull/102
Title: Add JWT authentication handler
Base: maiao.I111abc... ← Stacked on PR #101
Head: maiao.I222def...
Success! ✨
- Each PR is self-contained
- All tests pass at each step
- Reviewers can review schema changes separately from logic changes
- PRs are automatically stacked (PR #102 depends on PR #101)
Reviewer comments on PR #102 (second commit): "The variable name asdf is confusing"
# 1. Make the fix
vim auth/handler.go # Rename asdf → authToken
# 2. Create fixup commit
git add auth/handler.go
git commit --fixup HEAD
# Creates: "fixup! Add JWT authentication handler"
# 3. Update PRs
git reviewResult: PR #102 is automatically updated with your fix!
Reviewer comments on PR #101 (first commit): "The database query is inefficient"
# 1. Find the commit hash
git log --oneline
# cf5fd9a Add JWT authentication handler
# abc1234 Add user authentication schema ← Fix this one
# 2. Make the fix
vim db/schema.sql # Optimize the query
# 3. Create targeted fixup
git add db/schema.sql
git commit --fixup abc1234
# Creates: "fixup! Add user authentication schema"
# 4. Update PRs
git reviewWhat happens:
- Maiao detects the fixup commit
- Automatically groups it with the original commit (
abc1234) - Rebases and updates PR #101 with the optimization
- PR #102 is also rebased on top of the updated PR #101
Result: Both PRs updated correctly, maintaining the stack! 🎉
# Fix for first commit
git commit --fixup abc1234
# Fix for second commit
git commit --fixup cf5fd9a
# Another fix for first commit
git commit --fixup abc1234
# Apply all fixups
git reviewMaiao groups all fixups by their target commit and updates PRs accordingly.
To install maiao with homebrew, you need to tap the repo before installing it:
brew tap adevinta/maiao https://github.com/adevinta/maiao.git
brew install maiaoThe simplest way to install maiao is to download the binary from github. To do so, visit the releases page and download the version you want to install. Then run
mv <downloadsDir>/git-review-`uname -s`-`uname -m` /usr/local/bin/git-review
chmod +x /usr/local/bin/git-review
!!! warning "MacOS users"
To make sure the binary is not quarantined run: xattr -d com.apple.quarantine /usr/local/bin/git-review
To build a standalone binary, you will need to run go build -o /usr/local/bin/git-review ./cmd/maiao
The relevant Windows binary can be downloaded in the releases page
Download the git-review-windows-<arch> where <arch> is the architecture of your machine. If you are unsure, usually,
amd64 is the default windows architecture.
If you have ever installed and configured adv command line, you should already be good to go. If not, you will need to
configure a github token in your netrc file. To do so, follow these steps:
Create your personal access token with repo privileges and
create a file ~/.netrc having the following content:
machine github.company.example.com
login <firstname>.<lastname>@company.example.com
password <your token>
### Experimental system keychain
You may now use all the default keyrings supported by 99-designs/keyring.
To enable it, ensure you export the MAIAO_EXPERIMENTAL_CREDENTIALS=true environment variable before running git review.
Maiao will then use your system keyring like macOS keychain or pass
to store GitHub API keys.
We will be happy to receive your feedback about this feature in maiao's issues
Your environment is then ready to run maiao.
There will be some other configuration required for each repository you need to run maiao on. Don't worry, maiao will prompt and suggest you to perform the setup if the configuration is missing.
Every commit gets a unique identifier in its message:
Add user authentication
Implements JWT-based authentication
Change-Id: I8f3c2a1b5e9d7f6a4c3b2a1d0e9f8c7b6a5d4e3f
Purpose:
- Tracks commits across rebases
- Enables fixup commit matching
- Maps commits to GitHub branches
Generated by: Gerrit commit-msg hook (installed via git review install)
Each commit creates a branch: maiao.<Change-ID>
Example:
- Change-ID:
I8f3c2a1b5e9d7f6a4c3b2a1d0e9f8c7b6a5d4e3f - Branch:
maiao.I8f3c2a1b5e9d7f6a4c3b2a1d0e9f8c7b6a5d4e3f
These branches are ephemeral (recreated on each git review) and force-pushed.
PRs depend on each other:
main
└─ PR #1 (maiao.I111)
└─ PR #2 (maiao.I222)
└─ PR #3 (maiao.I333)
When PR #1 merges, git review automatically:
- Detects the merge
- Rebases remaining commits
- Updates PR #2 to target
maininstead ofmaiao.I111
git checkout main
git pull origin main
git checkout -b feature/my-feature
# Make commits...
git review# Make more commits
git commit -m "Additional changes"
git review # Updates existing PRs and creates new ones# Someone merged PR #1
git review # Automatically rebases remaining PRsgit fetch origin
git review # Automatically rebases if neededProblem: Commit-msg hook not installed or not working
Solution:
git review install # Reinstall hook
# Then amend your commits
git commit --amend --no-editProblem: Git remote has multiple URLs configured
Solution:
git remote -v # Check remotes
git remote set-url origin <single-url>Problem: Your branch has merge commits
Solution:
# Use rebase instead of merge
git rebase origin/mainProblem: Authentication issue
Solution:
# Check ~/.netrc has correct token
# Or verify GitHub token has 'repo' scope
# Recreate token: https://github.com/settings/tokensProblem: Fixup commit doesn't match any commit in branch
Solution:
git log --oneline # Find correct commit hash
# Recreate fixup with correct hash
git commit --fixup <correct-hash>Problem: Commits don't get Change-IDs
Solution:
# Check hook permissions
chmod +x .git/hooks/commit-msg
# Verify hook content
cat .git/hooks/commit-msgEach commit should be a complete, testable change:
✅ Good: "Add user authentication schema"
❌ Bad: "WIP changes"Commit in dependency order:
✅ First: Database schema
✅ Second: Business logic using schema
✅ Third: API endpoints using logicAll tests should pass at each commit:
git add file.go
go test ./... # ✅ Pass before committing
git commit -m "Add feature"Write clear commit messages:
✅ Good: "Add JWT token validation middleware
Validates JWT tokens from Authorization header
and rejects requests with invalid/expired tokens"
❌ Bad: "fix stuff"Don't amend commits directly; use fixups:
✅ git commit --fixup <hash> # Trackable, rebaseable
❌ git commit --amend # Loses Change-ID, breaks tracking- How Does It Work - Deep dive into technical details
- GitHub Issues - Report bugs or request features
- Contributing - Contribute to Maiao
