Submit, amend, and review Gerrit changes using git-review CLI. Use when asked to submit a patchset, download a change, rebase a change request, check CR status, or manage code reviews in Gerrit.
94
Quality
94%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
This document provides practical examples of common Gerrit code review workflows using git-review.
# Clone repository
git clone ssh://review.example.com:29418/myproject
cd myproject
# Setup git-review (one-time)
git review -s
# Verify setup
cat .gitreview# Install commit-msg hook for Change-Id generation
git review -s
# Verify hook is installed
ls -l .git/hooks/commit-msg
# Test with a commit
git commit --allow-empty -m "test: verify Change-Id generation"
git log -1
# Should see "Change-Id: I..." in commit message# Create feature branch
git checkout -b feature/new-api
git checkout main
# Make changes
vim api/endpoints.py
# Commit with descriptive message
git commit -m "Add new API endpoint for user profiles
This adds a REST endpoint that returns user profile data.
Change-Id: Ixxx # Auto-generated by commit hook
"
# Submit for review
git review# Group related changes under a topic
git commit -m "Part 1: Add user model"
git review -t user-profile-feature
# Additional changes in same topic
git checkout main
git checkout -b feature/user-api
# ... make changes ...
git commit -m "Part 2: Add user API"
git review -t user-profile-feature# Add reviewers when submitting
git review --reviewers alice@example.com,bob@example.com
# Add reviewers for specific areas
git review \
--reviewers backend-team@example.com \
--reviewers security-team@example.com# Submit as work-in-progress
git review -D
# Later, publish the change via Gerrit web UI
# or use SSH:
ssh -p 29418 review.example.com gerrit review 12345 --publish# See what would be submitted
git review -n
# Review the output, then submit
git review# Download specific change
git review -d 12345
# This creates branch: review/username/topic
git branch
# * review/alice/user-api
# main
# Review the code
git log -p HEAD^..HEAD
git diff main..HEAD
# Test the change
make test
python -m pytest
# Return to main
git checkout main# Download patchset 3 of change 12345
git review -d 12345,3
# Compare patchsets
git review -d 12345,2
PATCHSET_2=$(git rev-parse HEAD)
git review -d 12345,3
git diff $PATCHSET_2..HEAD# Apply change without creating branch
git checkout main
git review -x 12345
# Now the change is on main (don't push!)
# Test it
make test
# Discard when done
git reset --hard origin/main# Compare your local work to remote change
git review -m 12345
# Shows diff between your changes and the review# Download your change
git review -d 12345
# Make requested changes
vim api/endpoints.py
# Amend the commit (preserves Change-Id)
git commit --amend
# Upload new patchset
git review# Download your change
git review -d 12345
# Rebase on latest main
git fetch origin
git rebase origin/main
# Upload rebased patchset
git review# Submit exactly as-is (no rebase)
git review -R
# Useful when you've carefully tested specific commits# Download the change
git review -d 12345
# Reset to split commits
git reset HEAD^
# Create first commit
git add file1.py
git commit -m "Part 1: Add data model"
git review -t feature-name
# Create second commit
git add file2.py
git commit -m "Part 2: Add API endpoint"
git review -t feature-name# If you have multiple commits that should be one
git rebase -i HEAD~3
# In editor, change:
# pick abc123 commit 1
# pick def456 commit 2
# pick ghi789 commit 3
# To:
# pick abc123 commit 1
# squash def456 commit 2
# squash ghi789 commit 3
# Upload squashed change
git review# First change in series
git checkout main
git checkout -b feature/step1
# ... make changes ...
git commit -m "Step 1: Add database schema"
git review -t multi-step-feature
# Second change (depends on first)
git checkout -b feature/step2
# ... make changes ...
git commit -m "Step 2: Add API endpoint"
git review -t multi-step-feature
# Third change
git checkout -b feature/step3
# ... make changes ...
git commit -m "Step 3: Add UI components"
git review -t multi-step-feature# Download the middle change
git review -d 12346
# Make updates
git commit --amend
# Rebase dependent changes
git review
# Update dependent changes too
git review -d 12347
git rebase review/user/step2
git review# Query changes by topic
ssh -p 29418 review.example.com \
gerrit query topic:multi-step-feature status:open
# Or via web:
# https://review.example.com/#/q/topic:multi-step-feature# Create multiple independent changes
git checkout main
git checkout -b feature/change1
# ... work ...
git commit -m "Add feature 1"
git review
git checkout main
git checkout -b feature/change2
# ... work ...
git commit -m "Add feature 2"
git review
# List local review branches
git branch | grep review/# Forgot to amend, created new change instead
# Download the wrong new change
git review -d 12347
# Check both commits
git log -2 --oneline
# Squash them
git rebase -i HEAD~2
# Mark second commit as 'squash'
# Upload corrected change
git review# Download change from main
git review -d 12345
# Create backport branch
git checkout -b backport/stable-2.1 origin/stable/2.1
# Cherry-pick the change
git cherry-pick review/user/feature
# Resolve conflicts if needed
# ... fix conflicts ...
git add .
git cherry-pick --continue
# Submit to stable branch
git review# Submit and clean up local branch
git review -f
# This does:
# 1. Submits the change
# 2. Deletes local branch
# 3. Switches back to main# List open changes
ssh -p 29418 review.example.com gerrit query status:open
# List your changes
ssh -p 29418 review.example.com gerrit query owner:self status:open
# Find changes by topic
ssh -p 29418 review.example.com gerrit query topic:feature-name
# Complex query
ssh -p 29418 review.example.com gerrit query \
'project:myproject AND status:open AND -age:7d'
# Get JSON output
ssh -p 29418 review.example.com gerrit query \
--format=JSON status:open project:myproject# Approve change
ssh -p 29418 review.example.com gerrit review \
12345,3 --verified +1 --code-review +2 --message "'LGTM!'"
# Request changes
ssh -p 29418 review.example.com gerrit review \
12345,3 --code-review -1 --message "'Please fix the error handling'"
# Submit change
ssh -p 29418 review.example.com gerrit review \
12345,3 --submit# Abandon change
ssh -p 29418 review.example.com gerrit review \
12345 --abandon --message "'No longer needed'"
# Restore change
ssh -p 29418 review.example.com gerrit review \
12345 --restore --message "'Actually still needed'"# Set topic
ssh -p 29418 review.example.com gerrit set-topic \
12345 feature-name
# Remove topic
ssh -p 29418 review.example.com gerrit set-topic \
12345 ""#!/bin/bash
# Check your review queue each morning
echo "=== Changes Waiting for Your Review ==="
ssh -p 29418 review.example.com gerrit query \
--format=JSON \
'status:open AND reviewer:self -owner:self' | \
jq -r 'select(.number != null) | " #\(.number): \(.subject) (@\(.owner.username))"'
echo -e "\n=== Your Changes Needing Attention ==="
ssh -p 29418 review.example.com gerrit query \
--format=JSON \
'status:open AND owner:self' | \
jq -r 'select(.number != null) | " #\(.number): \(.subject) (Score: \(.currentPatchSet.approvals[0].value // "not reviewed"))"'#!/bin/bash
# Update all local review branches
for branch in $(git branch | grep review/); do
echo "Updating $branch..."
git checkout $branch
# Fetch latest patchset
CHANGE_NUM=$(git log -1 --format=%B | grep -oP 'Change-Id: I\K[a-f0-9]+' | head -1)
if [ -n "$CHANGE_NUM" ]; then
# Find change number from Change-Id
NUM=$(ssh -p 29418 review.example.com gerrit query \
--format=JSON change:I$CHANGE_NUM | \
jq -r 'select(.number != null) | .number')
if [ -n "$NUM" ]; then
git review -d $NUM
fi
fi
done
git checkout main#!/bin/bash
# Submit multiple changes in order
TOPIC="feature-name"
# Get all changes in topic, sorted by number
ssh -p 29418 review.example.com gerrit query \
--format=JSON topic:$TOPIC status:open | \
jq -r 'select(.number != null) | .number' | \
sort -n | \
while read num; do
echo "Reviewing change $num..."
# Download and test
git review -d $num
# Run tests
if make test; then
echo "Tests passed for $num"
# Approve via SSH
ssh -p 29418 review.example.com gerrit review \
$num --verified +1 --message "'Automated verification passed'"
else
echo "Tests failed for $num - stopping"
break
fi
done
git checkout main#!/bin/bash
# Monitor specific change for updates
CHANGE_NUM=$1
INTERVAL=${2:-60} # Check every 60 seconds
echo "Monitoring change $CHANGE_NUM..."
LAST_UPDATED=""
while true; do
INFO=$(ssh -p 29418 review.example.com gerrit query \
--format=JSON $CHANGE_NUM)
UPDATED=$(echo "$INFO" | jq -r '.lastUpdated')
STATUS=$(echo "$INFO" | jq -r '.status')
if [ "$UPDATED" != "$LAST_UPDATED" ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Change $CHANGE_NUM updated - Status: $STATUS"
# Show latest comments
echo "$INFO" | jq -r '.comments[]? | " \(.reviewer.username): \(.message)"'
LAST_UPDATED=$UPDATED
# Exit if merged or abandoned
if [ "$STATUS" = "MERGED" ] || [ "$STATUS" = "ABANDONED" ]; then
echo "Change $STATUS - exiting monitor"
break
fi
fi
sleep $INTERVAL
done#!/bin/bash
# Weekly activity report
WEEK_AGO=$(date -d '7 days ago' '+%Y-%m-%d')
echo "=== Gerrit Activity Report (last 7 days) ==="
echo -e "\nChanges Merged:"
ssh -p 29418 review.example.com gerrit query \
--format=JSON \
"status:merged AND after:$WEEK_AGO" | \
jq -r 'select(.number != null) | " #\(.number): \(.subject) (@\(.owner.username))"'
echo -e "\nChanges Abandoned:"
ssh -p 29418 review.example.com gerrit query \
--format=JSON \
"status:abandoned AND after:$WEEK_AGO" | \
jq -r 'select(.number != null) | " #\(.number): \(.subject) (@\(.owner.username))"'
echo -e "\nActive Changes (still open):"
ssh -p 29418 review.example.com gerrit query \
--format=JSON \
"status:open AND after:$WEEK_AGO" | \
jq -r 'select(.number != null) | " #\(.number): \(.subject) (@\(.owner.username))"'Add to ~/.gitconfig:
[alias]
# git-review shortcuts
gr = review
grd = review -d
grx = review -x
grf = review -f
grn = review -nUsage:
git gr # Submit review
git grd 12345 # Download change
git grx 12345 # Cherry-pick change
git grf # Submit and finishAdd to ~/.bashrc:
# Download and checkout Gerrit change
gr() {
git review -d "$1"
}
# Query Gerrit changes
gq() {
ssh -p 29418 review.example.com gerrit query "$@"
}
# Review from command line
gapprove() {
ssh -p 29418 review.example.com gerrit review \
"$1" --verified +1 --code-review +2 --message "'$2'"
}
# List my open changes
mychanges() {
ssh -p 29418 review.example.com gerrit query \
--format=JSON 'owner:self status:open' | \
jq -r 'select(.number != null) | "\(.number)\t\(.subject)"'
}# Set Gerrit username
export GERRIT_USER=myusername
# Create wrapper for SSH
gerrit() {
ssh -p 29418 $GERRIT_USER@review.example.com gerrit "$@"
}
# Usage:
gerrit query status:open
gerrit review 12345 --code-review +2Validate before pushing to Gerrit:
# .git/hooks/pre-push
#!/bin/bash
# Check for Change-Id
if ! git log -1 --format=%B | grep -q '^Change-Id:'; then
echo "Error: Missing Change-Id in commit message"
echo "Run: git review -s (to install commit-msg hook)"
exit 1
fi
# Run tests
if ! make test; then
echo "Error: Tests failed"
exit 1
fi
exit 0chmod +x .git/hooks/pre-pushInstall with Tessl CLI
npx tessl i odyssey4me/gerrit@0.2.2