# gh stack sync vs rebase: Understanding the Difference in GitHub's gh-stack

> Understand the difference between gh stack sync and gh stack rebase. Learn how sync updates your remote stack while rebase rearranges local commits without network ops.

- Repository: [GitHub/gh-stack](https://github.com/github/gh-stack)
- Tags: deep-dive
- Published: 2026-08-03

---

**`gh stack sync` is a full-cycle remote synchronization command that fetches, pushes, and updates GitHub's stack metadata, while `gh stack rebase` performs local-only cascading rebases to rearrange commits without network operations.**

When managing stacked pull requests with the `github/gh-stack` extension, choosing between these commands determines whether you are aligning your local repository with remote state or merely fixing local history. Both utilize the same `cascadeRebase` helper internally, but they differ fundamentally in their scope, network behavior, and side effects.

## What Does gh stack sync Do?

The `gh stack sync` command, implemented in [`cmd/sync.go`](https://github.com/github/gh-stack/blob/main/cmd/sync.go) within the `runSync` function, orchestrates a comprehensive synchronization between your local branches and the remote GitHub stack object. According to the command's long help text (lines 27-41), its primary goal is to bring the local stack and remote stack representation into complete agreement.

The implementation follows this sequence:

1. **Fetch** the latest remote refs from GitHub
2. **Reconcile** any remote-side stack changes via `reconcileRemoteStack`, pulling down new PR branches if they exist
3. **Fast-forward** branches that are behind their remote tracking refs
4. **Cascade-rebase** the stack if necessary, invoking the same `cascadeRebase` helper used by the rebase command
5. **Push** all branches atomically with `--force-with-lease`
6. **Sync PR state** and link pull requests into the remote stack object
7. **Prune** merged local branches when invoked with `--prune`

When conflicts occur during the cascade-rebase phase, the command aborts immediately and instructs the user to resolve them using `gh stack rebase` (lines 51-53). Upon successful completion, the command outputs either **"Stack synced"** (when the remote object is updated) or **"Branches synced"** (when only local branches are pushed), as implemented in lines 98-104.

## What Does gh stack rebase Do?

Defined in [`cmd/rebase.go`](https://github.com/github/gh-stack/blob/main/cmd/rebase.go) and executed via the `runRebase` function, `gh stack rebase` is strictly a local operation that rearranges commit history. As described in lines 53-60, it performs a cascading rebase so that each branch in the stack sits on top of its predecessor (or the trunk) without ever fetching from or pushing to the remote.

Key implementation characteristics include:

- **No network operations**: Skips fetching and pushing entirely
- **Cascading execution**: Calls `cascadeRebase` (lines 150-160) to sequentially rebase each branch onto its parent
- **Conflict state management**: On conflict, writes a state file named `gh-stack-rebase-state` (lines 62-66) to allow users to continue or abort later
- **Scope control**: Supports `--downstack` (trunk to current), `--upstack` (current to top), and `--no-trunk` flags (lines 48-55) to limit the rebase range

After a successful rebase, the command reminds you to run `gh stack push` to propagate changes, explicitly avoiding any modification of remote PR metadata.

## Key Differences: Remote Synchronization vs Local History

| Aspect | **`gh stack sync`** | **`gh stack rebase`** |
|--------|---------------------|----------------------|
| **Primary Goal** | Synchronize local state with remote GitHub stack object | Locally rearrange commits via cascading rebase |
| **Network Operations** | Fetches from remote and pushes with `--force-with-lease` | No fetch or push; local Git operations only |
| **Remote Metadata** | Updates and links PRs into the remote stack object | Does not modify remote PR state or stack file |
| **Conflict Resolution** | Aborts on conflict; defers to `gh stack rebase` | Saves state to file; supports `--continue` and `--abort` |
| **Typical Result** | Repository matches remote GitHub state exactly | Repository rebased locally; requires manual push |

## When to Use Each Command

### Use gh stack sync When You Need to Align with GitHub

Run this command when you need to ensure GitHub reflects your current local work. This includes scenarios where you have updated commits locally and need to push them, when teammates have modified the remote stack and you must reconcile changes, or when you want to prune merged branches using the `--prune` flag.

### Use gh stack rebase When You Need to Fix Local History

Execute this command when resolving diverging histories after pulling from upstream, when reordering commits within your stack before submitting, or when continuing a previously conflicted rebase using `--continue`. The `--downstack` and `--upstack` flags allow you to limit the operation to specific portions of the stack without affecting the entire chain.

## Handling Conflicts: Different Workflows

The commands diverge significantly in their approach to merge conflicts. In [`cmd/sync.go`](https://github.com/github/gh-stack/blob/main/cmd/sync.go) (lines 51-53), when the embedded `cascadeRebase` call encounters conflicts, `gh stack sync` immediately aborts the entire operation and directs you to run `gh stack rebase` for manual resolution.

Conversely, [`cmd/rebase.go`](https://github.com/github/gh-stack/blob/main/cmd/rebase.go) (lines 62-66) handles conflicts by writing the `gh-stack-rebase-state` file to disk, allowing you to resolve conflicts at your own pace and resume with `gh stack rebase --continue` or revert entirely with `gh stack rebase --abort`.

## Practical Code Examples

```bash

# Full synchronization: fetch, reconcile, rebase, push, and update PRs

gh stack sync
gh stack sync --prune

# Local-only rebase operations

gh stack rebase
gh stack rebase --downstack
gh stack rebase --upstack
gh stack rebase --no-trunk

# Conflict resolution during rebase

gh stack rebase --continue
gh stack rebase --abort

```

## Summary

- **`gh stack sync`** in [`cmd/sync.go`](https://github.com/github/gh-stack/blob/main/cmd/sync.go) is the comprehensive command that fetches remote changes, reconciles the stack object, pushes branches with `--force-with-lease`, and updates GitHub metadata
- **`gh stack rebase`** in [`cmd/rebase.go`](https://github.com/github/gh-stack/blob/main/cmd/rebase.go) is the local-only tool for cascading rebases that uses the `cascadeRebase` helper without network operations
- **Sync** aborts on conflicts and requires **rebase** for resolution; **rebase** provides `--continue` and `--abort` workflows via the `gh-stack-rebase-state` persistence file
- Use **sync** to align local and remote states; use **rebase** to manipulate local commit history before pushing

## Frequently Asked Questions

### Can I run gh stack rebase after gh stack sync fails?

Yes. When `gh stack sync` encounters rebase conflicts during its cascade phase (as implemented in [`cmd/sync.go`](https://github.com/github/gh-stack/blob/main/cmd/sync.go) lines 51-53), it aborts the entire synchronization and explicitly instructs you to run `gh stack rebase` to resolve the conflicts manually. After resolving the conflicts and completing the rebase with `--continue`, you can then run `gh stack sync` again to push the changes and update the remote stack object.

### Does gh stack sync automatically prune merged branches?

Only when invoked with the `--prune` flag. According to the implementation in [`cmd/sync.go`](https://github.com/github/gh-stack/blob/main/cmd/sync.go), the sync command includes an optional pruning step that deletes local branches for PRs that have been merged on GitHub. Without this flag, the command synchronizes active branches but leaves merged branches intact in your local repository.

### Why does gh stack rebase not update my pull requests on GitHub?

Because `gh stack rebase` (defined in [`cmd/rebase.go`](https://github.com/github/gh-stack/blob/main/cmd/rebase.go) lines 53-60) performs strictly local Git operations and deliberately does not push to the remote. After completing the cascading rebase, the command simply prints a reminder to push using `gh stack push` or `gh stack submit`. This design allows you to verify the rebased history before exposing rewritten commits to collaborators and updating the remote PR stack metadata.

### What happens to the stack state if I abort a rebase?

When you run `gh stack rebase --abort`, the command removes the `gh-stack-rebase-state` persistence file and restores the branches to their original positions recorded before the rebase began. This cleanup is managed within the `runRebase` function in [`cmd/rebase.go`](https://github.com/github/gh-stack/blob/main/cmd/rebase.go) (lines 62-66), ensuring your repository returns to a clean state without partial rebase artifacts.