Skip to content

Commit 8b7a74e

Browse files
SAY-5extrawurst
andauthored
fix: avoid index-out-of-bounds panic in staging apply_selection (#2955)
Co-authored-by: extrawurst <776816+extrawurst@users.noreply.github.com>
1 parent c6b370f commit 8b7a74e

2 files changed

Lines changed: 40 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
### Fixes
1616
* crash when opening submodule ([#2895](https://github.com/gitui-org/gitui/issues/2895))
1717
* when staging the last file in a directory, the first item after the directory is no longer skipped [[@Tillerino](https://github.com/Tillerino)] ([#2748](https://github.com/gitui-org/gitui/issues/2748))
18+
* index-out-of-bounds panic when unstaging lines near the end of a diff ([#2953](https://github.com/gitui-org/gitui/issues/2953))
1819

1920
## [0.28.1] - 2026-03-21
2021

asyncgit/src/sync/staging/mod.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,20 @@ impl NewFromOldContent {
3939
}
4040

4141
fn add_old_line(&mut self, old_lines: &[&str]) {
42-
self.lines.push(old_lines[self.old_index].to_string());
43-
self.old_index += 1;
42+
if let Some(line) = old_lines.get(self.old_index) {
43+
self.lines.push((*line).to_string());
44+
self.old_index += 1;
45+
}
4446
}
4547

4648
fn catchup_to_hunkstart(
4749
&mut self,
4850
hunk_start: usize,
4951
old_lines: &[&str],
5052
) {
51-
while hunk_start > self.old_index + 1 {
53+
while hunk_start > self.old_index + 1
54+
&& self.old_index < old_lines.len()
55+
{
5256
self.add_old_line(old_lines);
5357
}
5458
}
@@ -182,3 +186,35 @@ pub fn load_file(
182186

183187
Ok(res)
184188
}
189+
190+
#[cfg(test)]
191+
mod tests {
192+
use super::NewFromOldContent;
193+
194+
// Regression for #2953: indexing old_lines past its length used to panic
195+
// in add_old_line / catchup_to_hunkstart when a hunk_start pointed past
196+
// the end of the working copy. The bounds-checked helpers must stop
197+
// catching up at end-of-buffer instead of panicking.
198+
#[test]
199+
fn catchup_to_hunkstart_past_end_does_not_panic() {
200+
let old_lines = ["a", "b", "c"];
201+
let mut content = NewFromOldContent::default();
202+
203+
content.catchup_to_hunkstart(99, &old_lines);
204+
205+
assert_eq!(content.old_index, old_lines.len());
206+
assert_eq!(content.lines, vec!["a", "b", "c"]);
207+
}
208+
209+
#[test]
210+
fn add_old_line_at_end_is_noop() {
211+
let old_lines = ["only-line"];
212+
let mut content = NewFromOldContent::default();
213+
content.add_old_line(&old_lines);
214+
assert_eq!(content.old_index, 1);
215+
216+
content.add_old_line(&old_lines);
217+
assert_eq!(content.old_index, 1);
218+
assert_eq!(content.lines, vec!["only-line"]);
219+
}
220+
}

0 commit comments

Comments
 (0)