Highlight merge conflicts and provide for resolving them (#28065)

TODO:

- [x] Make it work in the project diff:
  - [x] Support non-singleton buffers
  - [x] Adjust excerpt boundaries to show full conflicts
- [x] Write tests for conflict-related events and state management
- [x] Prevent hunk buttons from appearing inside conflicts
- [x] Make sure it works over SSH, collab
- [x] Allow separate theming of markers

Bonus:

- [ ] Count of conflicts in toolbar
- [ ] Keyboard-driven navigation and resolution
- [ ] ~~Inlay hints to contextualize "ours"/"theirs"~~

Release Notes:

- Implemented initial support for resolving merge conflicts.

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
Cole Miller 2025-04-23 12:38:46 -04:00 committed by GitHub
parent ef54b58346
commit 724c935196
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 1626 additions and 184 deletions

View file

@ -34,6 +34,7 @@ pub struct FakeGitRepositoryState {
pub blames: HashMap<RepoPath, Blame>,
pub current_branch_name: Option<String>,
pub branches: HashSet<String>,
pub merge_head_shas: Vec<String>,
pub simulated_index_write_error_message: Option<String>,
}
@ -47,12 +48,20 @@ impl FakeGitRepositoryState {
blames: Default::default(),
current_branch_name: Default::default(),
branches: Default::default(),
merge_head_shas: Default::default(),
simulated_index_write_error_message: Default::default(),
}
}
}
impl FakeGitRepository {
fn with_state<F, T>(&self, write: bool, f: F) -> Result<T>
where
F: FnOnce(&mut FakeGitRepositoryState) -> T,
{
self.fs.with_git_state(&self.dot_git_path, write, f)
}
fn with_state_async<F, T>(&self, write: bool, f: F) -> BoxFuture<'static, Result<T>>
where
F: 'static + Send + FnOnce(&mut FakeGitRepositoryState) -> Result<T>,
@ -137,11 +146,18 @@ impl GitRepository for FakeGitRepository {
}
fn merge_head_shas(&self) -> Vec<String> {
vec![]
self.with_state(false, |state| state.merge_head_shas.clone())
.unwrap()
}
fn show(&self, _commit: String) -> BoxFuture<Result<CommitDetails>> {
unimplemented!()
fn show(&self, commit: String) -> BoxFuture<Result<CommitDetails>> {
async {
Ok(CommitDetails {
sha: commit.into(),
..Default::default()
})
}
.boxed()
}
fn reset(