zed/docs/src/git.md
Priyadharshan 4bdf188c99
Added Tracked , Staged options to stash (#62254)
# Objective

Closes #62252 

The Git Panel could only stash *everything* — `Stash All` runs
`git stash push --include-untracked`, sweeping tracked edits and
untracked files
into a single entry. There was no way to stash a subset, so the common
workflows
of "park my tracked edits but keep my new scratch files" and "park what
I've
staged and keep working on the rest" required dropping to the terminal.

## Images

<img width="389" height="358" alt="Screenshot 2026-08-10 at 3 10 50 PM"
src="https://github.com/user-attachments/assets/18e4c943-e320-4802-ada8-59e54bf4cefd"
/>

<img width="504" height="462" alt="Screenshot 2026-08-10 at 3 10 37 PM"
src="https://github.com/user-attachments/assets/783237eb-980d-47bc-a0f5-17b03a23a60c"
/>






## Solution

Add two stash variants alongside `Stash All`, surfaced in the Git
Panel's
overflow menu based on how the list is currently grouped, so the menu
mirrors the
sections the user can actually see:

| Group By | Stash entries offered |
| --- | --- |
| None | Stash All |
| Tracked & Untracked | Stash All, **Stash Tracked** |
| Staged & Unstaged | Stash All, **Stash Staged** |

- **`git::StashTracked`** stashes tracked changes and leaves untracked
files in
place. It reuses the existing pathspec plumbing
(`Repository::stash_entries`),
  filtering the status list down to the paths to stash.
- **`git::StashStaged`** stashes the index only, leaving unstaged
changes in
place. This *cannot* be expressed as a pathspec — a partially staged
file would
have its unstaged hunks stashed too — so it needs git's own `--staged`
flag.
  That meant a new `GitRepository::stash_staged` backend method and an
`optional bool staged` field on `proto::Stash` so remote projects work
too.

Both actions are unbound by default and are dispatchable from the
command palette
when the panel is focused.

One subtlety worth calling out for review: `Stash Tracked` filters on
`FileStatus::is_created()`, not `is_untracked()`. Staging a new file
flips it from
`Untracked` to `Tracked { Added }`, but the panel still lists it under
**Untracked** — using `is_untracked()` meant staged-new files were
silently
stashed. `is_created()` is the same predicate the panel uses to build
that section
(`git_panel.rs`), so the menu item and the list can no longer disagree.

This branch also includes a separate commit adding **per-section
staging**
(`git::StageSection` / `git::UnstageSection`) — right-click a file to
stage or
unstage every entry in its section. Happy to split that into its own PR
if
preferred.

## Testing

Manually tested on macOS against a scratch repo with a mix of states:
modified
tracked files, untracked files, and untracked files that had been
staged.

- `Stash Tracked` with tracked edits + untracked files → only tracked
edits
  stashed; untracked files remain.
- `Stash Tracked` with untracked files **staged** → they remain, staged.
This was
  broken in an earlier revision and drove the `is_created()` fix above.
- `Stash Staged` with one file staged and another modified-but-unstaged
→ only the
  staged file is stashed; the unstaged edit and untracked files survive.
- `Stash Pop` round-trips both cases back to the original state, with no
conflicts.
- Menu contents and disabled states verified in all three Group By
modes.
- Per-section staging covered by a new unit test,
  `test_stage_section_scopes_to_selected_section`.

Not covered by automated tests: the stash actions themselves.
`FakeGitRepository`
leaves every stash method `unimplemented!()`, so stash behavior isn't
reachable
from GPUI tests today — consistent with the existing untested
`StashAll`. Adding
fake-repo stash support looks like a worthwhile follow-up but felt out
of scope here.

Reviewers on non-macOS platforms: nothing here is platform-specific.
Note that
`Stash Staged` requires **git 2.35+** (Jan 2022) for `git stash push
--staged`;
older git surfaces a clear error toast rather than failing opaquely. The
remote
path (`proto::Stash.staged`) has not been exercised against a live
collab session.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- Added `Stash Tracked` and `Stash Staged` options to the Git Panel,
letting you stash only tracked changes or only staged changes.

---------

Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
2026-08-18 05:36:16 +00:00

23 KiB
Raw Permalink Blame History

description title
Zed is a text editor that supports lots of Git features Zed Editor Git integration documentation

Git

Zed has built-in Git support that lets you manage version control without leaving the editor. The Git Panel shows your working tree state, staging area, and branch information. Changes you make on the command line are reflected immediately in Zed.

For operations that Zed doesn't support natively, you can use the integrated terminal.

Repository Activation

When a project is rooted at a git repository, or at a subdirectory of one, all repositories in it are active immediately.

When a project is not rooted at a repository (a home directory, a folder of projects), repositories directly inside the project root are also active immediately, and deeper ones activate when a file inside them is opened, in local and remote projects alike.

Inactive repositories are fully indexed and searchable; only git features (status, diffs, branches) wait for activation.

This behavior is tied to the file_scan_depth setting, but not to its value: any non-zero file_scan_depth defers activation for repositories that are not directly inside a project root folder, and repositories rooted at or deeper than the limit are not even discovered until their directories are indexed on demand. In multi-folder projects, depth and activation are measured from each root folder separately.

Setting file_scan_depth to 0 turns deferred activation off: every discovered repository activates immediately.

Git Panel

The Git Panel shows the state of your working tree and Git's staging area.

You can open the Git Panel using {#action git_panel::ToggleFocus}, or by clicking the Git icon in the status bar.

In the panel you can see the state of your project at a glance: which repository and branch are active, what files have changed and the current staging state of each file.

Zed monitors your repository so that changes you make on the command line are instantly reflected.

Configuration

Open the Settings Editor (Cmd+, on macOS, Ctrl+, on Linux/Windows) to customize Git behavior. Settings are spread across two pages:

  • Panels > Git Panel: Panel position, tree vs flat view, status display style
  • Version Control: Gutter indicators, inline blame, hunk styles

Moving the Git Panel

By default, the Git Panel docks on the left. Go to Panels > Git Panel and change Git Panel Dock to move it to the right or bottom.

Switching to Tree View

The Git Panel shows a flat list of changed files by default. To see files organized by folder hierarchy instead, toggle Tree View in the panel's context menu, or enable it in Panels > Git Panel.

Inline Blame

Zed shows Git blame information on the current line. To turn this off or add a delay before it appears, go to Version Control > Inline Git Blame.

Hiding the Gutter Indicators

The colored bars in the gutter that show added, modified, and deleted lines can be hidden. Go to Version Control > Git Gutter and set Visibility to "Hide".

Commit Message Line Length

Zed wraps commit messages at 72 characters (a Git convention). To change this, search for "Git Commit" in Settings and adjust Preferred Line Length.

Project Diff

You can see all of the changes captured by Git in Zed by opening the Project Diff ({#kb git::Diff}), accessible via the {#action git::Diff} action in the Command Palette or the Git Panel.

All of the changes displayed in the Project Diff behave exactly the same as any other multibuffer: they are all editable excerpts of files.

You can stage or unstage each hunk as well as a whole file by hitting the buttons on the tab bar or their corresponding keybindings.

Word Diff Highlighting

By default, Zed highlights changed words within modified lines to make it easier to spot exactly what changed. To disable this globally, open the Settings Editor and go to Languages & Tools > Miscellaneous, then turn off Word Diff Enabled.

To disable word diff for specific languages only, add this to your settings.json:

{
  "languages": {
    "Markdown": {
      "word_diff_enabled": false
    }
  }
}

Diff View Styles

Zed displays diffs in two modes: split (side-by-side comparison) or unified (inline changes). Split view is the default.

Changing the diff view

Open the Settings Editor ({#kb zed::OpenSettings}) and search for "diff view style". Select either Split or Unified.

To change the default, add this to your settings.json:

{
  "diff_view_style": "unified"
}

See Configuring Zed for more about the Settings Editor.

Split vs unified

  • Split: Shows the original and modified versions side by side. Useful for comparing file structure or reviewing large changes.
  • Unified: Shows changes inline with additions and deletions in a single view. Useful for focusing on specific line changes.

You can switch between modes at any time. Your preference applies to Project Diff, File History, and Stash Diff View. These diff views function as multibuffers, allowing you to edit multiple excerpts simultaneously.

File History

File History shows the commit history for an individual file. Each entry displays the commit's author, timestamp, and message. Selecting a commit opens a diff view filtered to show only the changes made to that file in that commit.

To view File History:

  • Right-click on a file in the Project Panel and select "View File History"
  • Right-click on a file in the Git Panel and select "View File History"
  • Right-click on an editor tab and select "View File History"
  • Use the Command Palette and search for "file history"

Fetch, Push, and Pull

Fetch, push, or pull from your Git repository in Zed via the buttons available on the Git Panel or via the Command Palette by looking at the respective actions: {#action git::Fetch}, {#action git::Push}, and {#action git::Pull}.

Push Configuration

Zed respects Git's push configuration. When pushing, Zed checks the following in order:

  1. pushRemote configured for the current branch
  2. remote.pushDefault in your Git config
  3. The branch's tracking remote

This matches Git's standard behavior, so if you've configured pushRemote or pushDefault in your .gitconfig or via git config, Zed will use those settings.

Remotes

When your repository has multiple remotes, Zed shows a remote selector in the Git Panel. Click the remote button next to push/pull to choose which remote to use for that operation.

Staging Workflow

Zed has two primary staging workflows, using either the Project Diff or the panel directly.

Using the Project Diff

In the Project Diff view, you can focus on each hunk and stage them individually by clicking on the tab bar buttons or via the keybindings {#action git::StageAndNext} ({#kb git::StageAndNext}).

Similarly, stage all hunks at the same time with the {#action git::StageAll} ({#kb git::StageAll}) keybinding and then immediately commit with {#action git::Commit} ({#kb git::Commit}).

Using the Git Panel

From the panel, you can simply type a commit message and hit the commit button, or {#action git::Commit}. This will automatically stage all tracked files (indicated by a [·] in the entry's checkbox) and commit them.

Entries can be staged using each individual entry's checkbox. All changes can be staged using the button at the top of the panel, or {#action git::StageAll}.

Entries are grouped into sections (Tracked and Untracked by default), and each section can be staged on its own without touching the others. Click the checkbox on a section header, or use {#action git::StageSection} / {#action git::UnstageSection}, which act on the section containing the selected entry. These are also available by right-clicking an entry. Neither is bound by default; bind them in your keymap under the GitPanel context if you use them often.

To open a changed file in the editor without a diff view, right-click on the file in the Git Panel and select View File. Use Open Diff ({#kb menu::Confirm}) or Open Diff (File) to review changes in a diff view instead.

Committing

Zed offers two commit textareas:

  1. The first one is available right at the bottom of the Git Panel. Hitting {#kb git::Commit} immediately commits all of your staged changes.
  2. The second is available via the action {#action git::ExpandCommitEditor} or via hitting the {#kb git::ExpandCommitEditor} while focused in the Git Panel commit textarea.

Undoing a Commit

As soon as you commit in Zed, in the Git Panel, you'll see a bar right under the commit textarea, which will show the recently submitted commit. In there, you can use the "Uncommit" button, which performs the git reset HEADˆ--soft command.

Configuring Commit Line Length

By default, Zed sets the commit line length to 72 but it can be configured in your local settings.json file.

Find more information about setting the preferred-line-length in the Configuration section.

Branch Management

Creating and Switching Branches

Create a new branch using {#action git::Branch} or switch to an existing branch using {#action git::Switch} or {#action git::CheckoutBranch}.

When you are working in a Git worktree, use the branch picker after switching to the worktree to create or check out the branch you want to use there.

Deleting Branches

To delete a branch, open the branch switcher with {#action git::Switch}, find the branch you want to delete, and use the delete option. Zed will confirm before deleting to prevent accidental data loss.

Note: You cannot delete the branch you currently have checked out. Switch to a different branch first.

Git Worktrees

Git worktrees let you keep multiple checkouts of the same repository on disk at the same time. This is useful when you want to work on more than one branch or task without stashing, rebuilding, or disturbing the files in your main checkout.

Open the worktree picker from the title bar, next to the project picker, or by running {#action git::Worktree}. From the picker, you can:

  • Create a new linked worktree either from the current branch or default branch
  • Type a name to create a named worktree or let Zed automatically pick one for you
  • Switch the current workspace to an existing worktree
  • Open an existing worktree in a new window
  • Delete linked worktrees that are not currently open in the project

Worktree Management

New worktrees are created in detached HEAD state. After switching to the new worktree, use the branch picker next to the worktree picker to create a new branch or check out an existing, unused branch. This keeps Zed from accidentally checking out the same branch in multiple worktrees.

The directory used for new worktrees is controlled by the git.worktree_directory setting. By default, Zed creates worktrees under ../worktrees relative to the repository's working directory.

See All Settings for examples.

Init Setup

To run setup steps after Zed creates a linked worktree, use the create_worktree task hook. For agent-specific workflows, see Worktree Isolation.

Multi-root Workspaces

If your project contains multiple Git repositories (i.e., multi-root folders), Zed creates a linked worktree for each repository when creating a new worktree from the picker. Non-Git folders in the same project are included in the new workspace as-is.

Merge Conflicts

When you encounter merge conflicts after a merge, rebase, or pull, Zed highlights the conflicting regions in your files and displays resolution buttons above each conflict.

Viewing Conflicts

Conflicting files appear in the Git Panel with a warning icon. You can also see conflicts in the Project Diff view, where each conflict region is highlighted:

  • Changes from your current branch are highlighted in green
  • Changes from the incoming branch are highlighted in blue

Resolving Conflicts

Each conflict shows three buttons:

  • Use [branch-name]: Keep the changes from one branch (shows the actual branch name, like "main")
  • Use [other-branch]: Keep the changes from the other branch (like "feature-branch")
  • Use Both: Keep both sets of changes, with your branch's changes first

Click a button to resolve that conflict. The conflict markers are removed and replaced with your chosen content. After resolving all conflicts in a file, stage it and commit to complete the merge.

Tip: For complex conflicts that need manual editing, you can edit the file directly. Remove the conflict markers (<<<<<<<, =======, >>>>>>>) and keep the content you want.

Stashing

Git stash allows you to temporarily save your uncommitted changes and revert your working directory to a clean state. This is particularly useful when you need to quickly switch branches or pull updates without committing incomplete work.

Creating Stashes

To stash all your current changes, use the {#action git::StashAll} action. This will save both staged and unstaged changes, including untracked files, to a new stash entry and clean your working directory.

To stash only tracked changes and leave untracked files in place, use the {#action git::StashTracked} action. To stash only staged changes and leave unstaged changes in place, use the {#action git::StashStaged} action; this requires git 2.35 or newer.

The Git Panel's overflow menu offers whichever of these matches the current Group By mode: Stash Tracked when grouping by Tracked & Untracked, and Stash Staged when grouping by Staged & Unstaged. Stash All is always available.

Each of these actions first prompts for an optional stash name. Confirming with an empty prompt lets git generate its usual WIP on ... description.

Managing Stashes

Zed provides a stash picker accessible via {#action git::ViewStash} or from the Git Panel's overflow menu. From the stash picker, you can:

  • View stash list: Browse all your saved stashes with their descriptions and timestamps
  • Open diffs: See exactly what changes are stored in each stash
  • Apply stashes: Apply stash changes to your working directory while keeping the stash entry
  • Pop stashes: Apply stash changes and remove the stash entry from the list
  • Drop stashes: Delete unwanted stash entries without applying them

Quick Stash Operations

For faster workflows, Zed provides direct actions to work with the most recent stash:

  • Apply latest stash: Use {#action git::StashApply} to apply the most recent stash without removing it
  • Pop latest stash: Use {#action git::StashPop} to apply and remove the most recent stash

Stash Diff View

To view a stash's contents, select it in the stash picker and press {#kb stash_picker::ShowStashItem}. From the diff view, you can use these keybindings:

Action Keybinding
Apply stash {#kb git::ApplyCurrentStash}
Pop stash (apply and remove) {#kb git::PopCurrentStash}
Drop stash (remove without applying) {#kb git::DropCurrentStash}

AI Support in Git

Zed currently supports LLM-powered commit message generation. You can ask AI to generate a commit message by focusing on the message editor within the Git Panel and either clicking on the pencil icon in the bottom left, or reaching for the {#action git::GenerateCommitMessage}, or through the {#kb git::GenerateCommitMessage} keybinding.

Note that you need to have an LLM provider configured either via your own API keys or through Zed's hosted AI models. Visit AI Quick Start to learn how to configure AI.

You can specify your preferred model for this task by adding a commit_message_model field to your agent settings. See Feature-specific models for more information.

{
  "agent": {
    "commit_message_model": {
      "provider": "anthropic",
      "model": "claude-4-5-haiku"
    }
  }
}

To add custom commit instructions for the model, use the global AGENTS.md file located at ~/.config/zed/AGENTS.md on macOS and Linux, %APPDATA%\Zed\AGENTS.md on Windows.

To add custom instructions that apply only to commit message generation, use the commit_message_instructions field in your agent settings:

{
  "agent": {
    "commit_message_instructions": "Use the Conventional Commits format: <type>(<scope>): <description>."
  }
}

These instructions are sent to the model in addition to any instruction files, such as .rules or AGENTS.md. To add instructions that apply to both commit messages and the agent more broadly, use the global AGENTS.md file located at ~/.config/zed/AGENTS.md on macOS and Linux, %APPDATA%\Zed\AGENTS.md on Windows.

Before Zed v1.4.0, this was done through the Rules Library, which has been removed. See Migrating from Rules for more information.

Git Integrations

Zed integrates with popular Git hosting services to ensure that Git commit hashes and references to Issues, Pull Requests, and Merge Requests become clickable links.

Zed currently supports links to the hosted versions of GitHub, GitLab, Bitbucket, SourceHut and Codeberg.

Self-Hosted Instances

Zed automatically identifies Git hosting providers by checking for keywords in your Git remote URL. For example, if your self-hosted URL contains gitlab, gitea, or other recognized provider names, Zed will automatically register that hosting provider without any configuration needed.

However, if your self-hosted Git instance URL doesn't contain identifying keywords, you can manually configure Zed to create clickable links to your instance by adding a git_hosting_providers setting so commit hashes and permalinks resolve to your domain:

{
  "git_hosting_providers": [
    {
      "provider": "gitlab",
      "name": "Corp GitLab",
      "base_url": "https://git.example.corp"
    }
  ]
}

The provider field specifies which type of hosting service you're using. Supported provider values are github, gitlab, bitbucket, gitea, forgejo, and sourcehut. The name is optional and used as a display name for your instance, and base_url is the root URL of your self-hosted server.

You can configure multiple custom providers if you work with several self-hosted instances.

Zed also has a Copy Permalink feature to create a permanent link to a code snippet on your Git hosting service. These links are useful for sharing a specific line or range of lines in a file at a specific commit. Trigger this action via the Command Palette (search for permalink), by creating custom key bindings for the editor::CopyPermalinkToLine or editor::OpenPermalinkToLine actions or by simply right clicking and selecting Copy Permalink with line(s) selected in your editor.

Diff Hunk Keyboard Shortcuts

When viewing files with changes, Zed displays diff hunks that can be expanded or collapsed for detailed review:

  • Expand all diff hunks: {#action editor::ExpandAllDiffHunks} ({#kb editor::ExpandAllDiffHunks})
  • Collapse all diff hunks: Press Escape (bound to {#action editor::Cancel})
  • Toggle selected diff hunks: {#action editor::ToggleSelectedDiffHunks} ({#kb editor::ToggleSelectedDiffHunks})
  • Navigate between hunks: {#action editor::GoToHunk} and {#action editor::GoToPreviousHunk}

Tip: The Escape key is the quickest way to collapse all expanded diff hunks and return to an overview of your changes.

Action Reference

Action Keybinding
{#action git::Add} {#kb git::Add}
{#action git::StageAll} {#kb git::StageAll}
{#action git::UnstageAll} {#kb git::UnstageAll}
{#action git::StageSection} {#kb git::StageSection}
{#action git::UnstageSection} {#kb git::UnstageSection}
{#action git::ToggleStaged} {#kb git::ToggleStaged}
{#action git::StageAndNext} {#kb git::StageAndNext}
{#action git::UnstageAndNext} {#kb git::UnstageAndNext}
{#action git::Commit} {#kb git::Commit}
{#action git::ExpandCommitEditor} {#kb git::ExpandCommitEditor}
{#action git::Push} {#kb git::Push}
{#action git::ForcePush} {#kb git::ForcePush}
{#action git::Pull} {#kb git::Pull}
{#action git::PullRebase} {#kb git::PullRebase}
{#action git::Fetch} {#kb git::Fetch}
{#action git::Diff} {#kb git::Diff}
{#action git::Restore} {#kb git::Restore}
{#action git::RestoreFile} {#kb git::RestoreFile}
{#action git::Branch} {#kb git::Branch}
{#action git::Switch} {#kb git::Switch}
{#action git::CheckoutBranch} {#kb git::CheckoutBranch}
{#action git::Worktree} {#kb git::Worktree}
{#action git::Blame} {#kb git::Blame}
{#action git::StashAll} {#kb git::StashAll}
{#action git::StashTracked} {#kb git::StashTracked}
{#action git::StashStaged} {#kb git::StashStaged}
{#action git::StashPop} {#kb git::StashPop}
{#action git::StashApply} {#kb git::StashApply}
{#action git::ViewStash} {#kb git::ViewStash}
{#action editor::ToggleGitBlameInline} {#kb editor::ToggleGitBlameInline}
{#action editor::ExpandAllDiffHunks} {#kb editor::ExpandAllDiffHunks}
{#action editor::ToggleSelectedDiffHunks} {#kb editor::ToggleSelectedDiffHunks}

Not all actions have default keybindings, but can be bound by customizing your keymap.

Git CLI Configuration

If you would like to also use Zed for your git commit message editor when committing from the command line you can use zed --wait:

git config --global core.editor "zed --wait"

Or add the following to your shell environment (in ~/.zshrc, ~/.bashrc, etc):

export GIT_EDITOR="zed --wait"