just playing with tangled
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

conflicts: move `describe_conflict()` etc. onto `Conflict`

Before we had `conflicts::Conflict`, most of these functions took a
`backend::Conflict`. I think I didn't want to pollute the `backend`
module with this kind of logic, trying to keep it focused on
storage. Now that we have the type in `conflicts`, however, I think it
makes sense to move these functions onto it.

authored by

Martin von Zweigbergk and committed by
Martin von Zweigbergk
82883e64 7a8cabaf

+229 -231
+178 -186
lib/src/conflicts.rs
··· 128 128 .collect(); 129 129 backend::Conflict { removes, adds } 130 130 } 131 + 132 + pub fn materialize( 133 + &self, 134 + store: &Store, 135 + path: &RepoPath, 136 + output: &mut dyn Write, 137 + ) -> std::io::Result<()> { 138 + if let Some(file_conflict) = self.to_file_conflict() { 139 + let content = file_conflict.extract_as_single_hunk(store, path); 140 + materialize_merge_result(&content, output) 141 + } else { 142 + // Unless all terms are regular files, we can't do much better than to try to 143 + // describe the conflict. 144 + self.describe(output) 145 + } 146 + } 147 + 148 + pub fn to_file_conflict(&self) -> Option<Conflict<Option<FileId>>> { 149 + fn collect_file_terms(terms: &[Option<TreeValue>]) -> Option<Vec<Option<FileId>>> { 150 + let mut file_terms = vec![]; 151 + for term in terms { 152 + match term { 153 + None => { 154 + file_terms.push(None); 155 + } 156 + Some(TreeValue::File { 157 + id, 158 + executable: false, 159 + }) => { 160 + file_terms.push(Some(id.clone())); 161 + } 162 + _ => { 163 + return None; 164 + } 165 + } 166 + } 167 + Some(file_terms) 168 + } 169 + let file_removes = collect_file_terms(self.removes())?; 170 + let file_adds = collect_file_terms(self.adds())?; 171 + Some(Conflict::new(file_removes, file_adds)) 172 + } 173 + 174 + /// Give a summary description of the conflict's "removes" and "adds" 175 + pub fn describe(&self, file: &mut dyn Write) -> std::io::Result<()> { 176 + file.write_all(b"Conflict:\n")?; 177 + for term in self.removes().iter().flatten() { 178 + file.write_all(format!(" Removing {}\n", describe_conflict_term(term)).as_bytes())?; 179 + } 180 + for term in self.adds().iter().flatten() { 181 + file.write_all(format!(" Adding {}\n", describe_conflict_term(term)).as_bytes())?; 182 + } 183 + Ok(()) 184 + } 185 + 186 + /// Returns `None` if there are no conflict markers in `content`. 187 + pub fn update_from_content( 188 + &self, 189 + store: &Store, 190 + path: &RepoPath, 191 + content: &[u8], 192 + ) -> BackendResult<Option<Conflict<Option<TreeValue>>>> { 193 + // TODO: Check that the conflict only involves files and convert it to a 194 + // `Conflict<Option<FileId>>` so we can remove the wildcard pattern in the loops 195 + // further down. 196 + 197 + // First check if the new content is unchanged compared to the old content. If 198 + // it is, we don't need parse the content or write any new objects to the 199 + // store. This is also a way of making sure that unchanged tree/file 200 + // conflicts (for example) are not converted to regular files in the working 201 + // copy. 202 + let mut old_content = Vec::with_capacity(content.len()); 203 + self.materialize(store, path, &mut old_content).unwrap(); 204 + if content == old_content { 205 + return Ok(Some(self.clone())); 206 + } 207 + 208 + let mut removed_content = vec![vec![]; self.removes().len()]; 209 + let mut added_content = vec![vec![]; self.adds().len()]; 210 + // TODO: Change to let-else once our MSRV is above 1.65 211 + let hunks = 212 + if let Some(hunks) = parse_conflict(content, self.removes().len(), self.adds().len()) { 213 + hunks 214 + } else { 215 + // Either there are no self markers of they don't have the expected arity 216 + return Ok(None); 217 + }; 218 + for hunk in hunks { 219 + match hunk { 220 + MergeHunk::Resolved(slice) => { 221 + for buf in &mut removed_content { 222 + buf.extend_from_slice(&slice); 223 + } 224 + for buf in &mut added_content { 225 + buf.extend_from_slice(&slice); 226 + } 227 + } 228 + MergeHunk::Conflict(ConflictHunk { removes, adds }) => { 229 + for (i, buf) in removes.iter().enumerate() { 230 + removed_content[i].extend_from_slice(buf); 231 + } 232 + for (i, buf) in adds.iter().enumerate() { 233 + added_content[i].extend_from_slice(buf); 234 + } 235 + } 236 + } 237 + } 238 + // Now write the new files contents we found by parsing the file 239 + // with conflict markers. Update the Conflict object with the new 240 + // FileIds. 241 + let mut new_removes = vec![]; 242 + for (i, buf) in removed_content.iter().enumerate() { 243 + match &self.removes()[i] { 244 + Some(TreeValue::File { id: _, executable }) => { 245 + let file_id = store.write_file(path, &mut buf.as_slice())?; 246 + let new_value = TreeValue::File { 247 + id: file_id, 248 + executable: *executable, 249 + }; 250 + new_removes.push(Some(new_value)); 251 + } 252 + None if buf.is_empty() => { 253 + // The missing side of a conflict is still represented by 254 + // the empty string we materialized it as 255 + new_removes.push(None); 256 + } 257 + _ => { 258 + // The user edited a non-file side. This should never happen. We consider the 259 + // conflict resolved for now. 260 + return Ok(None); 261 + } 262 + } 263 + } 264 + let mut new_adds = vec![]; 265 + for (i, buf) in added_content.iter().enumerate() { 266 + match &self.adds()[i] { 267 + Some(TreeValue::File { id: _, executable }) => { 268 + let file_id = store.write_file(path, &mut buf.as_slice())?; 269 + let new_value = TreeValue::File { 270 + id: file_id, 271 + executable: *executable, 272 + }; 273 + new_adds.push(Some(new_value)); 274 + } 275 + None if buf.is_empty() => { 276 + // The missing side of a conflict is still represented by 277 + // the empty string we materialized it as => nothing to do 278 + new_adds.push(None); 279 + } 280 + _ => { 281 + // The user edited a non-file side. This should never happen. We consider the 282 + // conflict resolved for now. 283 + return Ok(None); 284 + } 285 + } 286 + } 287 + Ok(Some(Conflict::new(new_removes, new_adds))) 288 + } 289 + } 290 + 291 + impl Conflict<Option<FileId>> { 292 + pub fn extract_as_single_hunk(&self, store: &Store, path: &RepoPath) -> ConflictHunk { 293 + let removes_content = self 294 + .removes() 295 + .iter() 296 + .map(|term| get_file_contents(store, path, term)) 297 + .collect_vec(); 298 + let adds_content = self 299 + .adds() 300 + .iter() 301 + .map(|term| get_file_contents(store, path, term)) 302 + .collect_vec(); 303 + 304 + ConflictHunk { 305 + removes: removes_content, 306 + adds: adds_content, 307 + } 308 + } 131 309 } 132 310 133 311 fn describe_conflict_term(value: &TreeValue) -> String { ··· 159 337 } 160 338 } 161 339 162 - /// Give a summary description of a conflict's "removes" and "adds" 163 - pub fn describe_conflict( 164 - conflict: &Conflict<Option<TreeValue>>, 165 - file: &mut dyn Write, 166 - ) -> std::io::Result<()> { 167 - file.write_all(b"Conflict:\n")?; 168 - for term in conflict.removes().iter().flatten() { 169 - file.write_all(format!(" Removing {}\n", describe_conflict_term(term)).as_bytes())?; 170 - } 171 - for term in conflict.adds().iter().flatten() { 172 - file.write_all(format!(" Adding {}\n", describe_conflict_term(term)).as_bytes())?; 173 - } 174 - Ok(()) 175 - } 176 - 177 - pub fn to_file_conflict( 178 - conflict: &Conflict<Option<TreeValue>>, 179 - ) -> Option<Conflict<Option<FileId>>> { 180 - fn collect_file_terms(terms: &[Option<TreeValue>]) -> Option<Vec<Option<FileId>>> { 181 - let mut file_terms = vec![]; 182 - for term in terms { 183 - match term { 184 - None => { 185 - file_terms.push(None); 186 - } 187 - Some(TreeValue::File { 188 - id, 189 - executable: false, 190 - }) => { 191 - file_terms.push(Some(id.clone())); 192 - } 193 - _ => { 194 - return None; 195 - } 196 - } 197 - } 198 - Some(file_terms) 199 - } 200 - let file_removes = collect_file_terms(conflict.removes())?; 201 - let file_adds = collect_file_terms(conflict.adds())?; 202 - Some(Conflict::new(file_removes, file_adds)) 203 - } 204 - 205 340 fn get_file_contents(store: &Store, path: &RepoPath, term: &Option<FileId>) -> Vec<u8> { 206 341 match term { 207 342 Some(id) => { ··· 241 376 } 242 377 } 243 378 Ok(()) 244 - } 245 - 246 - pub fn materialize_conflict( 247 - store: &Store, 248 - path: &RepoPath, 249 - conflict: &Conflict<Option<TreeValue>>, 250 - output: &mut dyn Write, 251 - ) -> std::io::Result<()> { 252 - if let Some(file_conflict) = to_file_conflict(conflict) { 253 - let content = extract_file_conflict_as_single_hunk(store, path, &file_conflict); 254 - materialize_merge_result(&content, output) 255 - } else { 256 - // Unless all terms are regular files, we can't do much better than to try to 257 - // describe the conflict. 258 - describe_conflict(conflict, output) 259 - } 260 - } 261 - 262 - pub fn extract_file_conflict_as_single_hunk( 263 - store: &Store, 264 - path: &RepoPath, 265 - conflict: &Conflict<Option<FileId>>, 266 - ) -> ConflictHunk { 267 - let removes_content = conflict 268 - .removes() 269 - .iter() 270 - .map(|term| get_file_contents(store, path, term)) 271 - .collect_vec(); 272 - let adds_content = conflict 273 - .adds() 274 - .iter() 275 - .map(|term| get_file_contents(store, path, term)) 276 - .collect_vec(); 277 - 278 - ConflictHunk { 279 - removes: removes_content, 280 - adds: adds_content, 281 - } 282 379 } 283 380 284 381 pub fn materialize_merge_result( ··· 469 566 } 470 567 471 568 MergeHunk::Conflict(ConflictHunk { removes, adds }) 472 - } 473 - 474 - /// Returns `None` if there are no conflict markers in `content`. 475 - pub fn update_conflict_from_content( 476 - store: &Store, 477 - path: &RepoPath, 478 - conflict: &Conflict<Option<TreeValue>>, 479 - content: &[u8], 480 - ) -> BackendResult<Option<Conflict<Option<TreeValue>>>> { 481 - // TODO: Check that the conflict only involves files and convert it to a 482 - // `Conflict<Option<FileId>>` so we can remove the wildcard pattern in the loops 483 - // further down. 484 - 485 - // First check if the new content is unchanged compared to the old content. If 486 - // it is, we don't need parse the content or write any new objects to the 487 - // store. This is also a way of making sure that unchanged tree/file 488 - // conflicts (for example) are not converted to regular files in the working 489 - // copy. 490 - let mut old_content = Vec::with_capacity(content.len()); 491 - materialize_conflict(store, path, conflict, &mut old_content).unwrap(); 492 - if content == old_content { 493 - return Ok(Some(conflict.clone())); 494 - } 495 - 496 - let mut removed_content = vec![vec![]; conflict.removes().len()]; 497 - let mut added_content = vec![vec![]; conflict.adds().len()]; 498 - // TODO: Change to let-else once our MSRV is above 1.65 499 - let hunks = if let Some(hunks) = 500 - parse_conflict(content, conflict.removes().len(), conflict.adds().len()) 501 - { 502 - hunks 503 - } else { 504 - // Either there are no conflict markers of they don't have the expected arity 505 - return Ok(None); 506 - }; 507 - for hunk in hunks { 508 - match hunk { 509 - MergeHunk::Resolved(slice) => { 510 - for buf in &mut removed_content { 511 - buf.extend_from_slice(&slice); 512 - } 513 - for buf in &mut added_content { 514 - buf.extend_from_slice(&slice); 515 - } 516 - } 517 - MergeHunk::Conflict(ConflictHunk { removes, adds }) => { 518 - for (i, buf) in removes.iter().enumerate() { 519 - removed_content[i].extend_from_slice(buf); 520 - } 521 - for (i, buf) in adds.iter().enumerate() { 522 - added_content[i].extend_from_slice(buf); 523 - } 524 - } 525 - } 526 - } 527 - // Now write the new files contents we found by parsing the file 528 - // with conflict markers. Update the Conflict object with the new 529 - // FileIds. 530 - let mut new_removes = vec![]; 531 - for (i, buf) in removed_content.iter().enumerate() { 532 - match &conflict.removes()[i] { 533 - Some(TreeValue::File { id: _, executable }) => { 534 - let file_id = store.write_file(path, &mut buf.as_slice())?; 535 - let new_value = TreeValue::File { 536 - id: file_id, 537 - executable: *executable, 538 - }; 539 - new_removes.push(Some(new_value)); 540 - } 541 - None if buf.is_empty() => { 542 - // The missing side of a conflict is still represented by 543 - // the empty string we materialized it as 544 - new_removes.push(None); 545 - } 546 - _ => { 547 - // The user edited a non-file side. This should never happen. We consider the 548 - // conflict resolved for now. 549 - return Ok(None); 550 - } 551 - } 552 - } 553 - let mut new_adds = vec![]; 554 - for (i, buf) in added_content.iter().enumerate() { 555 - match &conflict.adds()[i] { 556 - Some(TreeValue::File { id: _, executable }) => { 557 - let file_id = store.write_file(path, &mut buf.as_slice())?; 558 - let new_value = TreeValue::File { 559 - id: file_id, 560 - executable: *executable, 561 - }; 562 - new_adds.push(Some(new_value)); 563 - } 564 - None if buf.is_empty() => { 565 - // The missing side of a conflict is still represented by 566 - // the empty string we materialized it as => nothing to do 567 - new_adds.push(None); 568 - } 569 - _ => { 570 - // The user edited a non-file side. This should never happen. We consider the 571 - // conflict resolved for now. 572 - return Ok(None); 573 - } 574 - } 575 - } 576 - Ok(Some(Conflict::new(new_removes, new_adds))) 577 569 } 578 570 579 571 #[cfg(test)]
+5 -9
lib/src/working_copy.rs
··· 34 34 use crate::backend::{ 35 35 BackendError, ConflictId, FileId, MillisSinceEpoch, ObjectId, SymlinkId, TreeId, TreeValue, 36 36 }; 37 - use crate::conflicts::{materialize_conflict, update_conflict_from_content}; 38 37 use crate::gitignore::GitIgnoreFile; 39 38 use crate::lock::FileLock; 40 39 use crate::matchers::{DifferenceMatcher, Matcher, PrefixMatcher}; ··· 659 658 let mut content = vec![]; 660 659 file.read_to_end(&mut content).unwrap(); 661 660 let conflict = self.store.read_conflict(&repo_path, conflict_id)?; 662 - if let Some(new_conflict) = update_conflict_from_content( 663 - self.store.as_ref(), 664 - &repo_path, 665 - &conflict, 666 - &content, 667 - ) 668 - .unwrap() 661 + if let Some(new_conflict) = conflict 662 + .update_from_content(self.store.as_ref(), &repo_path, &content) 663 + .unwrap() 669 664 { 670 665 new_file_state.file_type = FileType::Conflict; 671 666 *current_file_state = new_file_state; ··· 792 787 err, 793 788 })?; 794 789 let mut conflict_data = vec![]; 795 - materialize_conflict(self.store.as_ref(), path, &conflict, &mut conflict_data) 790 + conflict 791 + .materialize(self.store.as_ref(), path, &mut conflict_data) 796 792 .expect("Failed to materialize conflict to in-memory buffer"); 797 793 file.write_all(&conflict_data) 798 794 .map_err(|err| CheckoutError::IoError {
+29 -22
lib/tests/test_conflicts.rs
··· 13 13 // limitations under the License. 14 14 15 15 use jujutsu_lib::backend::{FileId, TreeValue}; 16 - use jujutsu_lib::conflicts::{ 17 - materialize_conflict, parse_conflict, update_conflict_from_content, Conflict, 18 - }; 16 + use jujutsu_lib::conflicts::{parse_conflict, Conflict}; 19 17 use jujutsu_lib::repo::Repo; 20 18 use jujutsu_lib::repo_path::RepoPath; 21 19 use jujutsu_lib::store::Store; ··· 290 288 vec![Some(file_value(&left_id)), Some(file_value(&right_id))], 291 289 ); 292 290 let mut result: Vec<u8> = vec![]; 293 - materialize_conflict(store, &path, &conflict, &mut result).unwrap(); 291 + conflict.materialize(store, &path, &mut result).unwrap(); 294 292 insta::assert_snapshot!( 295 293 String::from_utf8(result.clone()).unwrap(), 296 294 @r###" ··· 648 646 // If the content is unchanged compared to the materialized value, we get the 649 647 // old conflict id back. 650 648 let mut materialized = vec![]; 651 - materialize_conflict(store, &path, &conflict, &mut materialized).unwrap(); 652 - let result = update_conflict_from_content(store, &path, &conflict, &materialized).unwrap(); 649 + conflict 650 + .materialize(store, &path, &mut materialized) 651 + .unwrap(); 652 + let result = conflict 653 + .update_from_content(store, &path, &materialized) 654 + .unwrap(); 653 655 assert_eq!(result, Some(conflict.clone())); 654 656 655 657 // If the conflict is resolved, we get None back to indicate that. 656 - let result = 657 - update_conflict_from_content(store, &path, &conflict, b"resolved 1\nline 2\nresolved 3\n") 658 - .unwrap(); 658 + let result = conflict 659 + .update_from_content(store, &path, b"resolved 1\nline 2\nresolved 3\n") 660 + .unwrap(); 659 661 assert_eq!(result, None); 660 662 661 663 // If the conflict is partially resolved, we get a new conflict back. 662 - let result = update_conflict_from_content( 663 - store, 664 - &path, 665 - &conflict, 666 - b"resolved 1\nline 2\n<<<<<<<\n%%%%%%%\n-line 3\n+left 3\n+++++++\nright 3\n>>>>>>>\n", 667 - ) 668 - .unwrap(); 664 + let result = conflict 665 + .update_from_content( 666 + store, 667 + &path, 668 + b"resolved 1\nline 2\n<<<<<<<\n%%%%%%%\n-line 3\n+left 3\n+++++++\nright 3\n>>>>>>>\n", 669 + ) 670 + .unwrap(); 669 671 let new_conflict = result.unwrap(); 670 672 assert_ne!(new_conflict, conflict); 671 673 // Calculate expected new FileIds ··· 700 702 // If the content is unchanged compared to the materialized value, we get the 701 703 // old conflict id back. 702 704 let mut materialized = vec![]; 703 - materialize_conflict(store, &path, &conflict, &mut materialized).unwrap(); 704 - let result = update_conflict_from_content(store, &path, &conflict, &materialized).unwrap(); 705 + conflict 706 + .materialize(store, &path, &mut materialized) 707 + .unwrap(); 708 + let result = conflict 709 + .update_from_content(store, &path, &materialized) 710 + .unwrap(); 705 711 assert_eq!(result, Some(conflict.clone())); 706 712 707 713 // If the conflict is resolved, we get None back to indicate that. 708 - let result = update_conflict_from_content(store, &path, &conflict, b"resolved\n").unwrap(); 714 + let result = conflict 715 + .update_from_content(store, &path, b"resolved\n") 716 + .unwrap(); 709 717 assert_eq!(result, None); 710 718 711 719 // If the conflict is modified, we get a new conflict back. 712 - let result = update_conflict_from_content( 720 + let result = conflict.update_from_content( 713 721 store, 714 722 &path, 715 - &conflict, 716 723 b"<<<<<<<\n%%%%%%%\n line 1\n-line 2 before\n+line 2 modified after\n line 3\n+++++++\n>>>>>>>\n", 717 724 ) 718 725 .unwrap(); ··· 737 744 conflict: &Conflict<Option<TreeValue>>, 738 745 ) -> String { 739 746 let mut result: Vec<u8> = vec![]; 740 - materialize_conflict(store, path, conflict, &mut result).unwrap(); 747 + conflict.materialize(store, path, &mut result).unwrap(); 741 748 String::from_utf8(result).unwrap() 742 749 }
+4 -2
src/commands/mod.rs
··· 47 47 use jujutsu_lib::settings::UserSettings; 48 48 use jujutsu_lib::tree::{merge_trees, Tree}; 49 49 use jujutsu_lib::workspace::Workspace; 50 - use jujutsu_lib::{conflicts, file_util, revset}; 50 + use jujutsu_lib::{file_util, revset}; 51 51 use maplit::{hashmap, hashset}; 52 52 53 53 use crate::cli_util::{ ··· 1290 1290 Some(TreeValue::Conflict(id)) => { 1291 1291 let conflict = repo.store().read_conflict(&path, &id)?; 1292 1292 let mut contents = vec![]; 1293 - conflicts::materialize_conflict(repo.store(), &path, &conflict, &mut contents).unwrap(); 1293 + conflict 1294 + .materialize(repo.store(), &path, &mut contents) 1295 + .unwrap(); 1294 1296 ui.request_pager(); 1295 1297 ui.stdout_formatter().write_all(&contents)?; 1296 1298 }
+7 -3
src/diff_util.rs
··· 27 27 use jujutsu_lib::repo_path::RepoPath; 28 28 use jujutsu_lib::settings::UserSettings; 29 29 use jujutsu_lib::tree::{Tree, TreeDiffIterator}; 30 - use jujutsu_lib::{conflicts, diff, files, rewrite, tree}; 30 + use jujutsu_lib::{diff, files, rewrite, tree}; 31 31 32 32 use crate::cli_util::{CommandError, WorkspaceCommandHelper}; 33 33 use crate::formatter::Formatter; ··· 307 307 TreeValue::Conflict(id) => { 308 308 let conflict = repo.store().read_conflict(path, id).unwrap(); 309 309 let mut content = vec![]; 310 - conflicts::materialize_conflict(repo.store(), path, &conflict, &mut content).unwrap(); 310 + conflict 311 + .materialize(repo.store(), path, &mut content) 312 + .unwrap(); 311 313 Ok(content) 312 314 } 313 315 } ··· 456 458 mode = "100644".to_string(); 457 459 hash = id.hex(); 458 460 let conflict = repo.store().read_conflict(path, id).unwrap(); 459 - conflicts::materialize_conflict(repo.store(), path, &conflict, &mut content).unwrap(); 461 + conflict 462 + .materialize(repo.store(), path, &mut content) 463 + .unwrap(); 460 464 } 461 465 } 462 466 let hash = hash[0..10].to_string();
+6 -9
src/merge_tools.rs
··· 22 22 use config::ConfigError; 23 23 use itertools::Itertools; 24 24 use jujutsu_lib::backend::{TreeId, TreeValue}; 25 - use jujutsu_lib::conflicts::{ 26 - describe_conflict, extract_file_conflict_as_single_hunk, materialize_merge_result, 27 - to_file_conflict, update_conflict_from_content, 28 - }; 25 + use jujutsu_lib::conflicts::materialize_merge_result; 29 26 use jujutsu_lib::gitignore::GitIgnoreFile; 30 27 use jujutsu_lib::matchers::EverythingMatcher; 31 28 use jujutsu_lib::repo_path::RepoPath; ··· 161 158 None => return Err(ConflictResolveError::PathNotFoundError(repo_path.clone())), 162 159 }; 163 160 let conflict = tree.store().read_conflict(repo_path, &conflict_id)?; 164 - let file_conflict = to_file_conflict(&conflict).ok_or_else(|| { 161 + let file_conflict = conflict.to_file_conflict().ok_or_else(|| { 165 162 let mut summary_bytes: Vec<u8> = vec![]; 166 - describe_conflict(&conflict, &mut summary_bytes) 163 + conflict 164 + .describe(&mut summary_bytes) 167 165 .expect("Writing to an in-memory buffer should never fail"); 168 166 ConflictResolveError::NotNormalFilesError( 169 167 repo_path.clone(), ··· 177 175 sides: file_conflict.adds().len(), 178 176 }); 179 177 }; 180 - let mut content = extract_file_conflict_as_single_hunk(tree.store(), repo_path, &file_conflict); 178 + let mut content = file_conflict.extract_as_single_hunk(tree.store(), repo_path); 181 179 182 180 let editor = get_merge_tool_from_settings(ui, settings)?; 183 181 let initial_output_content: Vec<u8> = if editor.merge_tool_edits_conflict_markers { ··· 246 244 247 245 let mut new_tree_value: Option<TreeValue> = None; 248 246 if editor.merge_tool_edits_conflict_markers { 249 - if let Some(new_conflict) = update_conflict_from_content( 247 + if let Some(new_conflict) = conflict.update_from_content( 250 248 tree.store(), 251 249 repo_path, 252 - &conflict, 253 250 output_file_contents.as_slice(), 254 251 )? { 255 252 let new_conflict_id = tree.store().write_conflict(repo_path, &new_conflict)?;