git: Add diff view for stash entries (#38280)

Continues the work from #35927 to add a git diff view for stash entries.

[Screencast From 2025-09-17
19-46-01.webm](https://github.com/user-attachments/assets/ded33782-adef-4696-8e34-3665911c09c7)

Stash entries are [represented as
commits](https://git-scm.com/docs/git-stash#_discussion) except they
have up to 3 parents:

```
       .----W (this is the stash entry)
      /    /|
-----H----I |
           \|
            U
```

Where `H` is the `HEAD` commit, `I` is a commit that records the state
of the index, and `U` is another commit that records untracked files
(when using `git stash -u`).

Given this, I modified the existing commit view struct to allow loading
stash and commits entries with git sha identifier so that we can get a
similar git diff view for both of them.

The stash diff is generated by comparing the stash commit with its
parent (`<commit>^` or `H` in the diagram) which generates the same diff
as doing `git stash show -p <stash entry>`. This *can* be
counter-intuitive since a user may expect the comparison to be made
between the stash commit and the current commit (`HEAD`), but given that
the default behavior in git cli is to compare with the stash parent, I
went for that approach.

Hoping to get some feedback from a Zed team member to see if they agree
with this approach.

Release Notes:

- Add git diff view for stash entries
- Add toolbar on git diff view for stash entries
- Prompt before executing a destructive stash action on diff view
- Fix commit view for merge commits  (see #38289)
This commit is contained in:
Alvaro Parker
2025-10-20 11:47:15 -04:00
committed by GitHub
parent eda7a49f01
commit db404fc2e3
11 changed files with 461 additions and 57 deletions
+4 -4
View File
@@ -693,10 +693,11 @@ impl GitRepository for RealGitRepository {
.args([
"--no-optional-locks",
"show",
"--format=%P",
"--format=",
"-z",
"--no-renames",
"--name-status",
"--first-parent",
])
.arg(&commit)
.stdin(Stdio::null())
@@ -707,9 +708,8 @@ impl GitRepository for RealGitRepository {
.context("starting git show process")?;
let show_stdout = String::from_utf8_lossy(&show_output.stdout);
let mut lines = show_stdout.split('\n');
let parent_sha = lines.next().unwrap().trim().trim_end_matches('\0');
let changes = parse_git_diff_name_status(lines.next().unwrap_or(""));
let changes = parse_git_diff_name_status(&show_stdout);
let parent_sha = format!("{}^", commit);
let mut cat_file_process = util::command::new_smol_command(&git_binary_path)
.current_dir(&working_directory)