Git context menu (#24844)

Adds the non-entry specific right click menu to the panel, and the
features contained therin:

* Stage all
* Discard Tracked Changes
* Trash Untracked Files

Also changes the naming from "Changes"/"New" to better match Git's
terminology (though not convinced on this, it was awkward to describe
"Discard Changes" without a way to distinguish between the changes and
the files containing them).

Release Notes:

- N/A
This commit is contained in:
Conrad Irwin 2025-02-14 14:04:32 -07:00 committed by GitHub
parent bd105a5fc7
commit be83074243
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 488 additions and 82 deletions

View file

@ -37,7 +37,8 @@ actions!(
// editor::RevertSelectedHunks
StageAll,
UnstageAll,
RevertAll,
DiscardTrackedChanges,
TrashUntrackedFiles,
Uncommit,
Commit,
ClearCommitMessage

View file

@ -111,6 +111,7 @@ pub trait GitRepository: Send + Sync {
fn branch_exits(&self, _: &str) -> Result<bool>;
fn reset(&self, commit: &str, mode: ResetMode) -> Result<()>;
fn checkout_files(&self, commit: &str, paths: &[RepoPath]) -> Result<()>;
fn show(&self, commit: &str) -> Result<CommitDetails>;
@ -233,6 +234,31 @@ impl GitRepository for RealGitRepository {
Ok(())
}
fn checkout_files(&self, commit: &str, paths: &[RepoPath]) -> Result<()> {
if paths.is_empty() {
return Ok(());
}
let working_directory = self
.repository
.lock()
.workdir()
.context("failed to read git work directory")?
.to_path_buf();
let output = new_std_command(&self.git_binary_path)
.current_dir(&working_directory)
.args(["checkout", commit, "--"])
.args(paths.iter().map(|path| path.as_ref()))
.output()?;
if !output.status.success() {
return Err(anyhow!(
"Failed to checkout files:\n{}",
String::from_utf8_lossy(&output.stderr)
));
}
Ok(())
}
fn load_index_text(&self, path: &RepoPath) -> Option<String> {
fn logic(repo: &git2::Repository, path: &RepoPath) -> Result<Option<String>> {
const STAGE_NORMAL: i32 = 0;
@ -617,6 +643,10 @@ impl GitRepository for FakeGitRepository {
unimplemented!()
}
fn checkout_files(&self, _: &str, _: &[RepoPath]) -> Result<()> {
unimplemented!()
}
fn path(&self) -> PathBuf {
let state = self.state.lock();
state.path.clone()