gt land¶
Merge approved pull requests into the trunk branch.
Landing is the final step in the stacked diff workflow. It merges a PR via the provider API (GitHub or GitLab), deletes the local branch, and updates dependent branches.
Usage¶
Arguments¶
| Argument | Description |
|---|---|
[branch] |
Branch to land (default: current branch) |
Options¶
| Option | Description |
|---|---|
--dry-run |
Show what would happen without making changes |
--all |
Land all approved PRs in the stack |
--stack <name> |
Land all approved PRs in a specific stack |
--method <method> |
Merge method: merge, squash, or rebase |
--delete-branch |
Delete the branch after landing (default: true) |
--no-delete-branch |
Keep the branch after landing |
--skip-ci |
Merge even if CI hasn't finished |
--force |
Land even if the PR isn't fully approved |
Examples¶
Land Current Branch¶
Merges the PR for the current branch and deletes the local branch.
Land Specific Branch¶
You don't need to be on the branch to land it.
Land All Approved PRs¶
Stack processes them in dependency order (bottom to top), lands each one, and syncs between landings.
Land a Specific Stack¶
Choose Merge Method¶
# Create a merge commit
gt land --method merge
# Squash into a single commit (default for most repos)
gt land --method squash
# Rebase commits onto trunk
gt land --method rebase
The default method respects your repository settings on GitHub/GitLab. You can override it per landing.
Preview Landing¶
Output:
Would land:
feature/auth-models (PR #42, 2 approvals, CI passing)
Delete local branch: feature/auth-models
Restack feature/auth-api onto main
Restack feature/auth-ui onto feature/auth-api
Keep Branch After Landing¶
The branch is merged but remains locally. Stack still removes it from tracking metadata.
Skip CI Check¶
# Land even if CI is still running
# (Use with caution — only if you know the build will pass)
gt land --skip-ci
Force Landing¶
# Land even if the PR doesn't have required approvals
# (Requires maintainer permissions on the provider)
gt land --force
Behavior¶
Step-by-Step¶
1. VALIDATE PR STATUS
- Query provider for PR #N status
- Check approvals count
- Check CI status (unless --skip-ci)
- Verify no merge conflicts
2. MERGE
- Call provider API to merge PR #N
- Use specified merge method (or repo default)
- Wait for merge confirmation
3. CLEAN UP LOCAL
- Delete local branch (unless --no-delete-branch)
- Remove Stack metadata for the branch
- Update parent relationships for children
4. SYNC CHILDREN
- If children exist:
Update their parent to the landed branch's parent
Restack children onto the new parent
5. REPORT
- Show merge commit SHA
- List deleted branches
- List restacked branches
Landing Order¶
Stack enforces dependency order. Given:
You can land in this order:
1. feature/a (PR #1) — bottom-most
2. feature/b (PR #2) — after #1 lands
3. feature/c (PR #3) — after #2 lands
You cannot land feature/c first because its base branch (feature/b) hasn't been merged yet.
Landing via Provider API¶
Stack uses the provider API to merge:
GitHub:
# Equivalent API call:
curl -X PUT \
-H "Authorization: token $TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/org/repo/pulls/42/merge \
-d '{"merge_method":"squash"}'
GitLab:
# Equivalent API call:
curl -X PUT \
-H "PRIVATE-TOKEN: $TOKEN" \
https://gitlab.com/api/v4/projects/123/merge_requests/42/merge \
-d '{"squash":true}'
Merge Methods¶
Squash (Default)¶
All commits in the branch are squashed into a single commit on trunk:
Before:
main: M1 ── M2
\
A1 ── A2 ── A3 (feature/a)
After squash:
main: M1 ── M2 ── S1
Where S1 contains all changes from A1, A2, A3
Pros: Clean history, one commit per PR Cons: Loses individual commit history
Merge Commit¶
A merge commit is created, preserving branch history:
Before:
main: M1 ── M2
\
A1 ── A2 ── A3 (feature/a)
After merge:
main: M1 ── M2 ── M3 (merge commit)
\ /
A1 ── A2 ── A3
Pros: Preserves full history, shows parallel work Cons: More complex history, merge commits in trunk
Rebase¶
Commits are rebased onto trunk and fast-forwarded:
Before:
main: M1 ── M2
\
A1 ── A2 ── A3 (feature/a)
After rebase:
main: M1 ── M2 ── A1' ── A2' ── A3'
Pros: Linear history, no merge commits Cons: Rewrites commit SHAs, can be confusing
Choosing a Method¶
| Scenario | Recommended Method |
|---|---|
| Small PR (1-3 commits) | Squash |
| Large feature with meaningful commits | Merge commit |
| Team prefers linear history | Rebase |
| Individual commits tell a story | Merge commit |
| "One logical change" per PR | Squash |
Handling Failures¶
Merge Conflict on Landing¶
If someone merged to trunk while you were preparing to land:
$ gt land feature/auth-models
Merging PR #42...
ERROR: Merge conflict detected on main.
# Sync to get latest main
gt sync
# If the landed branch now has conflicts:
gt restack
gt submit # Update the PR with resolved state
# Re-approve if needed, then land again
gt land feature/auth-models
CI Failure¶
$ gt land feature/auth-models
ERROR: CI checks failed for PR #42.
# Fix the issue
gt checkout feature/auth-models
# Make changes...
git add .
gt modify
gt submit # Updates PR #42
# Wait for CI to pass, then land
gt land feature/auth-models
Approval Required¶
$ gt land feature/auth-models
ERROR: PR #42 requires 2 approvals, has 1.
# Ping reviewers or wait
# Or use --force (if you have permissions)
gt land feature/auth-models --force
Branch Protection¶
$ gt land feature/auth-models
ERROR: Branch protection prevents direct merge.
# Check repository settings
# You may need to use the web UI
# Or configure Stack to use a merge queue
Landing Multiple PRs¶
Sequential Landing¶
# Land the bottom PR
gt land feature/step-1
# Sync updates children
gt sync
# Land the next
gt land feature/step-2
# Sync again
gt sync
# Land the next
gt land feature/step-3
Batch Landing¶
This lands them in order automatically, syncing between each landing.
Landing with Gaps¶
If you want to land PR #1 and #3 but not #2:
# Land #1
gt land feature/step-1
gt sync
# Update #3 to target main directly
gt checkout feature/step-3
gt track feature/step-3 --parent main
gt restack
gt submit
# Now #3 targets main, not #2
gt land feature/step-3
After Landing¶
What Happens to Child Branches¶
After landing a parent:
Before:
main
└── feature/a PR #1
└── feature/b PR #2
└── feature/c PR #3
After landing feature/a:
main ───────────────── feature/a (merged)
└── feature/b PR #2 (now targets main)
└── feature/c PR #3 (now targets feature/b)
feature/b automatically retargets to main because its old base (feature/a) no longer exists.
Cleaning Up¶
After landing all PRs in a stack:
# All branches are merged and deleted
gt log
# main (no active stacks)
# Clean up any stale remote branches
git fetch --prune origin
Tips for Smooth Landing¶
- Land promptly: Approved PRs that sit accumulate conflicts
- Sync before landing: Ensure you have the latest trunk state
- Use
--dry-runfirst: Preview what will happen - Check CI status: Don't land failing PRs
- Communicate with team: Let reviewers know you're about to land
- Land bottom-up: Children can't land before parents
- Use squash for small PRs: Keeps history clean
- Batch with
--all: When everything is approved, land it all
Related Commands¶
gt submit- Create or update PRsgt sync- Sync after landinggt restack- Rebase branches