import DefinitionCard from "@site/src/components/DefinitionCard";

# Force-with-lease

_Refuse a force push when the remote tip has moved past what you expect._

After you rewrite local history with [rebase](./merge-vs-rebase) or amend, a normal push is rejected because the remote tip is not an ancestor of your new tip. A [force push](/docs/how-to/pushing-to-remote) overwrites that remote tip. `--force-with-lease` still overwrites, but only when the remote matches the tip your repository already expects.

Treq Push uses that same check. You do not pass the Git flag.

## Introduction

`--force` updates a remote branch to your local tip with no check against concurrent work. If a collaborator pushed while you rebased, those commits disappear from the branch.

`--force-with-lease` treats the update as conditional. Git compares the live remote tip to an expected value, usually the tip stored in your remote-tracking branch. Matching hashes allow the update. Diverging hashes reject the push and leave the upstream commits in place.

## Understanding the Concept

A **force push** replaces a remote branch pointer. History rewriting needs that replacement when the rewritten commits no longer descend from the previous remote tip. Unconditional force push ignores whether anyone else moved the branch.

**Force-with-lease** adds a compare-and-swap check before that replacement. The lease is the expected remote value. Git asks the remote to update the ref only if that expected value still matches the live tip.

The default expected value comes from your **remote-tracking branch**, such as `refs/remotes/origin/feature`. That ref is a local cache of what `git fetch` last saw on the remote. The lease compares that cached hash to the remote tip at push time.

| Form | Expected tip | Scope |
| --- | --- | --- |
| `--force-with-lease` | Each destination's remote-tracking branch | Every ref this push updates |
| `--force-with-lease=` | That ref's remote-tracking branch | Only the named ref |
| `--force-with-lease=:` | The commit you name | Only the named ref, ignoring the tracking cache |

The `:` form hard-codes the expected hash. Scripts use it when the remote-tracking cache is unreliable or when several jobs might race on the same branch.

## Applying It in Practice

Rewrite a private feature branch, then update the remote only if nobody else pushed:

```bash
git fetch origin
git switch feature/my-work
git rebase origin/main
git push --force-with-lease origin feature/my-work
```

Protect one named branch during a multi-ref push:

```bash
git push --force-with-lease=feature/my-work origin feature/my-work notes/commits
```

In CI, pin the expected tip to a hash you already validated:

```bash
git push --force-with-lease=feature/my-work:a3f9c12 origin feature/my-work
```

If the lease fails, fetch and inspect the remote branch before deciding whether to rebase onto the new tip or recover the other person's commits.

In Treq you do not run those Git commands. Each [workspace](/docs/concepts/workspaces) has its own bookmark. **Push** on the workspace card asks Jujutsu to update that bookmark on `origin`. Jujutsu sends the tip it last stored for the remote and your new local tip.

The Git server accepts the write only when those tips match. That is the `--force-with-lease` check. Treq does not offer an unconditional `--force` on Push.

**Sync** fetches first, then runs the same Push path. If the bookmark diverged, Treq rebases your local mutable commits onto the remote tip before that push. The new commits then sit on the current remote tip, so the push is usually a fast-forward.

If you already rewrote history and the remote did not move, Sync still uses the lease. It does not skip the compare.

The Treq CLI has no force-push path. Workspace Push and Sync are the rewrite-safe remote updates.

## Engineering Considerations

The default lease trusts your remote-tracking branch. Background `git fetch` from an IDE, terminal helper, or cron job can advance that cache to the live remote tip without merging those commits into your working branch. The lease then passes, and a force push can overwrite work you never integrated locally.

Treq can fetch when you open a workspace. That fetch updates the stored remote tip the same way. Push then has the same limit as default `--force-with-lease`. Use Sync when you need remote commits in the workspace before you overwrite the branch.

`--force-if-includes` closes that gap when combined with `--force-with-lease`. Before allowing the update, Git checks that the tip of the remote-tracking branch is reachable from one of your local branch's reflog entries. If those remote commits are absent from that history, the push is rejected even when the tip hashes match.

```bash
git push --force-with-lease --force-if-includes origin feature/my-work
```

`--force-if-includes` is a no-op with `--force-with-lease=:`, because that form already names the expected tip instead of reading the tracking cache. Other mitigations include a push-only remote that never receives fetches into the same tracking refs, or an explicit `:` value taken from a known good commit.

Do not use any force push form on protected shared branches such as `main` or release lines. Prefer lease-protected force pushes for branches with a single owner, an agreed rewrite policy, or a [dependent stack](./stacked-prs) whose owner rebases every layer together.

## Scaling and Operations

Treat rewriteable feature branches as owned resources. Document who may rebase them and whether reviewers should base follow-up work on the remote tip.

In automation, prefer `--force-with-lease=:` over the parameterless form. A fixed expected hash survives background fetches and concurrent jobs that update tracking refs between validation and push.

For interactive developer defaults, pair parameterless `--force-with-lease` with `--force-if-includes` where your Git version supports it. Branch protection on hosting platforms should still reject force pushes to shared integration branches.

When a lease rejection appears in CI, fail the job and surface the unexpected remote tip. Silent retries with a refreshed expect value recreate the race the lease exists to prevent.

## Next Steps

- [Cherry-pick vs Rebase](./cherry-pick-vs-rebase): distinguish selected patch copying from branch rewriting
- [Pushing to Remote](/docs/how-to/pushing-to-remote): push rewritten branches from the Treq workflow
- [Auto-rebase AI Branches](/learn/how-to/auto-rebase-ai-branches): keep dependent agent branches current without dropping lease protection
- [Stacked PR Workflow](/learn/workflows/git/stacked-pr): force-push rewritten stacks without clobbering dependents
