asyncgit/sync/staging/
stage_tracked.rs

1use super::apply_selection;
2use crate::{
3	error::{Error, Result},
4	sync::{
5		diff::DiffLinePosition, patches::get_file_diff_patch,
6		patches::patch_get_hunklines, repository::repo, RepoPath,
7	},
8};
9use easy_cast::Conv;
10use scopetime::scope_time;
11use std::path::Path;
12
13///
14pub fn stage_lines(
15	repo_path: &RepoPath,
16	file_path: &str,
17	is_stage: bool,
18	lines: &[DiffLinePosition],
19) -> Result<()> {
20	scope_time!("stage_lines");
21
22	if lines.is_empty() {
23		return Ok(());
24	}
25
26	let repo = repo(repo_path)?;
27	// log::debug!("stage_lines: {:?}", lines);
28
29	let mut index = repo.index()?;
30	index.read(true)?;
31	let mut idx =
32		index.get_path(Path::new(file_path), 0).ok_or_else(|| {
33			Error::Generic(String::from(
34				"only non new files supported",
35			))
36		})?;
37	let blob = repo.find_blob(idx.id)?;
38	let indexed_content = String::from_utf8(blob.content().into())?;
39
40	let new_content = {
41		let patch =
42			get_file_diff_patch(&repo, file_path, is_stage, false)?;
43		let hunks = patch_get_hunklines(&patch)?;
44
45		let old_lines = indexed_content.lines().collect::<Vec<_>>();
46
47		apply_selection(lines, &hunks, &old_lines, is_stage, false)?
48	};
49
50	let blob_id = repo.blob(new_content.as_bytes())?;
51
52	idx.id = blob_id;
53	idx.file_size = u32::try_conv(new_content.len())?;
54	index.add(&idx)?;
55
56	index.write()?;
57	index.read(true)?;
58
59	Ok(())
60}
61
62#[cfg(test)]
63mod test {
64	use super::*;
65	use crate::sync::{
66		diff::get_diff,
67		tests::{get_statuses, repo_init, write_commit_file},
68		utils::{repo_write_file, stage_add_file},
69	};
70
71	#[test]
72	fn test_stage() {
73		static FILE_1: &str = r"0
74";
75
76		static FILE_2: &str = r"0
771
782
793
80";
81
82		let (path, repo) = repo_init().unwrap();
83		let path: &RepoPath = &path.path().to_str().unwrap().into();
84
85		write_commit_file(&repo, "test.txt", FILE_1, "c1");
86
87		repo_write_file(&repo, "test.txt", FILE_2).unwrap();
88
89		stage_lines(
90			path,
91			"test.txt",
92			false,
93			&[DiffLinePosition {
94				old_lineno: None,
95				new_lineno: Some(2),
96			}],
97		)
98		.unwrap();
99
100		let diff = get_diff(path, "test.txt", true, None).unwrap();
101
102		assert_eq!(diff.lines, 3);
103		assert_eq!(&*diff.hunks[0].lines[0].content, "@@ -1 +1,2 @@");
104	}
105
106	#[test]
107	fn test_panic_stage_no_newline() {
108		static FILE_1: &str = r"a = 1
109b = 2";
110
111		static FILE_2: &str = r"a = 2
112b = 3
113c = 4";
114
115		let (path, repo) = repo_init().unwrap();
116		let path: &RepoPath = &path.path().to_str().unwrap().into();
117
118		write_commit_file(&repo, "test.txt", FILE_1, "c1");
119
120		repo_write_file(&repo, "test.txt", FILE_2).unwrap();
121
122		stage_lines(
123			path,
124			"test.txt",
125			false,
126			&[
127				DiffLinePosition {
128					old_lineno: Some(1),
129					new_lineno: None,
130				},
131				DiffLinePosition {
132					old_lineno: Some(2),
133					new_lineno: None,
134				},
135			],
136		)
137		.unwrap();
138
139		let diff = get_diff(path, "test.txt", true, None).unwrap();
140
141		assert_eq!(diff.lines, 5);
142		assert_eq!(&*diff.hunks[0].lines[0].content, "@@ -1,2 +1 @@");
143	}
144
145	#[test]
146	fn test_unstage() {
147		static FILE_1: &str = r"0
148";
149
150		static FILE_2: &str = r"0
1511
1522
1533
154";
155
156		let (path, repo) = repo_init().unwrap();
157		let path: &RepoPath = &path.path().to_str().unwrap().into();
158
159		write_commit_file(&repo, "test.txt", FILE_1, "c1");
160
161		repo_write_file(&repo, "test.txt", FILE_2).unwrap();
162
163		assert_eq!(get_statuses(path), (1, 0));
164
165		stage_add_file(path, Path::new("test.txt")).unwrap();
166
167		assert_eq!(get_statuses(path), (0, 1));
168
169		let diff_before =
170			get_diff(path, "test.txt", true, None).unwrap();
171
172		assert_eq!(diff_before.lines, 5);
173
174		stage_lines(
175			path,
176			"test.txt",
177			true,
178			&[DiffLinePosition {
179				old_lineno: None,
180				new_lineno: Some(2),
181			}],
182		)
183		.unwrap();
184
185		assert_eq!(get_statuses(path), (1, 1));
186
187		let diff = get_diff(path, "test.txt", true, None).unwrap();
188
189		assert_eq!(diff.lines, 4);
190	}
191}