From 56c3cdb6e604ecb48e82c0831d00b137a1eabff5 Mon Sep 17 00:00:00 2001 From: wiiznokes <78230769+wiiznokes@users.noreply.github.com> Date: Mon, 5 Jan 2026 16:56:26 +0100 Subject: [PATCH 1/5] init --- app/src/main/rust/.gitignore | 3 +- app/src/main/rust/src/lib.rs | 15 ++- app/src/main/rust/src/libgit2/mod.rs | 3 + app/src/main/rust/src/libgit2/test_merge.rs | 134 ++++++++++++++++++++ 4 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 app/src/main/rust/src/libgit2/test_merge.rs diff --git a/app/src/main/rust/.gitignore b/app/src/main/rust/.gitignore index 1d161777..a1fb3cb8 100644 --- a/app/src/main/rust/.gitignore +++ b/app/src/main/rust/.gitignore @@ -1,3 +1,4 @@ target/ /openssl-prebuild/*/install -/vendor \ No newline at end of file +/vendor +/repo_test \ No newline at end of file diff --git a/app/src/main/rust/src/lib.rs b/app/src/main/rust/src/lib.rs index 8ea3a400..c13e79c8 100644 --- a/app/src/main/rust/src/lib.rs +++ b/app/src/main/rust/src/lib.rs @@ -1,6 +1,7 @@ use std::fmt::{Debug, Display}; use anyhow::anyhow; +use git2::Signature; use jni::JNIEnv; use jni::objects::{JClass, JObject, JString, JValue}; use jni::sys::{jboolean, jint, jobject, jstring}; @@ -147,6 +148,15 @@ pub struct GitAuthor { pub email: String, } +impl<'a> From> for GitAuthor { + fn from(value: Signature<'a>) -> Self { + GitAuthor { + name: value.name().unwrap_or("").to_string(), + email: value.email().unwrap_or("").to_string(), + } + } +} + impl Debug for Cred { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -381,10 +391,7 @@ pub extern "C" fn Java_io_github_wiiznokes_gitnote_manager_GitManagerKt_pullLib< let cred = Cred::from_jni(&mut env, &cred).unwrap(); let name: String = env.get_string(&name).unwrap().into(); let email: String = env.get_string(&email).unwrap().into(); - let author = GitAuthor { - name, - email - }; + let author = GitAuthor { name, email }; unwrap_or_log!(libgit2::pull(cred, &author), "pull"); OK } diff --git a/app/src/main/rust/src/libgit2/mod.rs b/app/src/main/rust/src/libgit2/mod.rs index b5f4a980..358c2023 100644 --- a/app/src/main/rust/src/libgit2/mod.rs +++ b/app/src/main/rust/src/libgit2/mod.rs @@ -18,6 +18,9 @@ mod merge; #[cfg(test)] mod test; +#[cfg(test)] +mod test_merge; + const REMOTE: &str = "origin"; static REPO: LazyLock>> = LazyLock::new(|| Mutex::new(None)); diff --git a/app/src/main/rust/src/libgit2/test_merge.rs b/app/src/main/rust/src/libgit2/test_merge.rs new file mode 100644 index 00000000..c1826c2e --- /dev/null +++ b/app/src/main/rust/src/libgit2/test_merge.rs @@ -0,0 +1,134 @@ +use std::{fs, io, path::Path}; + +use git2::{AnnotatedCommit, Oid, Repository, Signature}; + +use crate::{GitAuthor, libgit2::merge::do_merge}; + +fn clear_dir>(path: P) -> io::Result<()> { + for entry in fs::read_dir(path)? { + let entry = entry?; + let path = entry.path(); + + if path.is_dir() { + fs::remove_dir_all(path)?; + } else { + fs::remove_file(path)?; + } + } + Ok(()) +} + +fn signature() -> Signature<'static> { + Signature::now("Test", "test@example.com").unwrap() +} + +fn create_initial_commit(repo: &Repository) -> Oid { + let sig = signature(); + + let tree_id = { + let mut index = repo.index().unwrap(); + index.write_tree().unwrap() + }; + + let tree = repo.find_tree(tree_id).unwrap(); + + repo.commit(Some("HEAD"), &sig, &sig, "initial commit", &tree, &[]) + .unwrap() +} + +fn create_branch_commit(repo: &Repository, branch: &str, parent: Oid) -> Oid { + let sig = signature(); + let parent = repo.find_commit(parent).unwrap(); + + let tree_id = { + let mut index = repo.index().unwrap(); + index.write_tree().unwrap() + }; + let tree = repo.find_tree(tree_id).unwrap(); + + let commit_id = repo + .commit(None, &sig, &sig, "branch commit", &tree, &[&parent]) + .unwrap(); + + repo.branch(branch, &repo.find_commit(commit_id).unwrap(), false) + .unwrap(); + + commit_id +} + +fn annotated_commit(repo: &Repository, oid: Oid) -> AnnotatedCommit<'_> { + repo.find_annotated_commit(oid).unwrap() +} + +pub fn commit_file( + repo: &Repository, + path: &Path, + contents: &str, + message: &str, + branch: &str, +) -> Oid { + repo.set_head(&format!("refs/heads/{branch}")).unwrap(); + repo.checkout_head(None).unwrap(); + + // Write file to working tree + let full_path = repo.workdir().unwrap().join(path); + fs::create_dir_all(full_path.parent().unwrap()).unwrap(); + fs::write(&full_path, contents).unwrap(); + + // Stage file + let mut index = repo.index().unwrap(); + index.add_path(path).unwrap(); + let tree_id = index.write_tree().unwrap(); + let tree = repo.find_tree(tree_id).unwrap(); + + // Signature + let sig = signature(); + + // Parent commit (if any) + let parents = match repo.head() { + Ok(head) => vec![repo.find_commit(head.target().unwrap()).unwrap()], + Err(_) => vec![], // initial commit + }; + + let parent_refs: Vec<&git2::Commit> = parents.iter().collect(); + + // Commit + let oid = repo + .commit(Some("HEAD"), &sig, &sig, message, &tree, &parent_refs) + .unwrap(); + + oid +} + +#[test] +fn do_test() { + let path = "repo_test/repo1"; + + let _ = clear_dir(path); + + let _ = fs::create_dir_all(path); + + let repo = Repository::init(path).unwrap(); + + let base = create_initial_commit(&repo); + + let oid1 = commit_file(&repo, Path::new("file1.txt"), "hello", "file 1", "master"); + + let branch_commit = create_branch_commit(&repo, "dev", base); + + let oid2 = commit_file( + &repo, + Path::new("file1.txt"), + "hello world", + "update file 1", + "master", + ); + + let oid3 = commit_file(&repo, Path::new("file2.txt"), "hello", "file 2", "dev"); + + let author = GitAuthor::from(signature()); + + let annotated = annotated_commit(&repo, branch_commit); + + do_merge(&repo, "dev", annotated, &author).unwrap(); +} From 521a7ed111c6b56bd28ad09b00b4650929e0ec1a Mon Sep 17 00:00:00 2001 From: wiiznokes <78230769+wiiznokes@users.noreply.github.com> Date: Tue, 6 Jan 2026 14:32:49 +0100 Subject: [PATCH 2/5] Update test_merge.rs --- app/src/main/rust/src/libgit2/test_merge.rs | 62 ++++++++++----------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/app/src/main/rust/src/libgit2/test_merge.rs b/app/src/main/rust/src/libgit2/test_merge.rs index c1826c2e..392221e2 100644 --- a/app/src/main/rust/src/libgit2/test_merge.rs +++ b/app/src/main/rust/src/libgit2/test_merge.rs @@ -19,7 +19,7 @@ fn clear_dir>(path: P) -> io::Result<()> { } fn signature() -> Signature<'static> { - Signature::now("Test", "test@example.com").unwrap() + Signature::now("Moi", "test@example.com").unwrap() } fn create_initial_commit(repo: &Repository) -> Oid { @@ -36,28 +36,16 @@ fn create_initial_commit(repo: &Repository) -> Oid { .unwrap() } -fn create_branch_commit(repo: &Repository, branch: &str, parent: Oid) -> Oid { - let sig = signature(); +fn create_branch(repo: &Repository, branch: &str, parent: Oid) { let parent = repo.find_commit(parent).unwrap(); - - let tree_id = { - let mut index = repo.index().unwrap(); - index.write_tree().unwrap() - }; - let tree = repo.find_tree(tree_id).unwrap(); - - let commit_id = repo - .commit(None, &sig, &sig, "branch commit", &tree, &[&parent]) - .unwrap(); - - repo.branch(branch, &repo.find_commit(commit_id).unwrap(), false) - .unwrap(); - - commit_id + repo.branch(branch, &parent, false).unwrap(); } -fn annotated_commit(repo: &Repository, oid: Oid) -> AnnotatedCommit<'_> { - repo.find_annotated_commit(oid).unwrap() +fn annotated_commit<'a>(repo: &'a Repository, branch: &str) -> AnnotatedCommit<'a> { + let dev_ref = repo + .find_reference(&format!("refs/heads/{branch}")) + .unwrap(); + repo.reference_to_annotated_commit(&dev_ref).unwrap() } pub fn commit_file( @@ -67,9 +55,6 @@ pub fn commit_file( message: &str, branch: &str, ) -> Oid { - repo.set_head(&format!("refs/heads/{branch}")).unwrap(); - repo.checkout_head(None).unwrap(); - // Write file to working tree let full_path = repo.workdir().unwrap().join(path); fs::create_dir_all(full_path.parent().unwrap()).unwrap(); @@ -85,21 +70,33 @@ pub fn commit_file( let sig = signature(); // Parent commit (if any) - let parents = match repo.head() { - Ok(head) => vec![repo.find_commit(head.target().unwrap()).unwrap()], - Err(_) => vec![], // initial commit + let parents = match repo.find_reference(&format!("refs/heads/{branch}")) { + Ok(r) => vec![repo.find_commit(r.target().unwrap()).unwrap()], + Err(_) => vec![], }; let parent_refs: Vec<&git2::Commit> = parents.iter().collect(); // Commit let oid = repo - .commit(Some("HEAD"), &sig, &sig, message, &tree, &parent_refs) + .commit( + Some(&format!("refs/heads/{branch}")), + &sig, + &sig, + message, + &tree, + &parent_refs, + ) .unwrap(); oid } +fn set_head(repo: &Repository, branch: &str) { + repo.set_head(&format!("refs/heads/{branch}")).unwrap(); + repo.checkout_head(None).unwrap(); +} + #[test] fn do_test() { let path = "repo_test/repo1"; @@ -110,13 +107,11 @@ fn do_test() { let repo = Repository::init(path).unwrap(); - let base = create_initial_commit(&repo); - let oid1 = commit_file(&repo, Path::new("file1.txt"), "hello", "file 1", "master"); - let branch_commit = create_branch_commit(&repo, "dev", base); + create_branch(&repo, "dev", oid1); - let oid2 = commit_file( + let _oid2 = commit_file( &repo, Path::new("file1.txt"), "hello world", @@ -124,11 +119,12 @@ fn do_test() { "master", ); - let oid3 = commit_file(&repo, Path::new("file2.txt"), "hello", "file 2", "dev"); + let _oid3 = commit_file(&repo, Path::new("file2.txt"), "hello", "file 2", "dev"); let author = GitAuthor::from(signature()); - let annotated = annotated_commit(&repo, branch_commit); + let annotated = annotated_commit(&repo, "dev"); + set_head(&repo, "master"); do_merge(&repo, "dev", annotated, &author).unwrap(); } From 3335b25061ff1a64a5a145e47ec7ae8a285d91df Mon Sep 17 00:00:00 2001 From: wiiznokes <78230769+wiiznokes@users.noreply.github.com> Date: Tue, 6 Jan 2026 14:58:13 +0100 Subject: [PATCH 3/5] ff --- app/src/main/rust/src/libgit2/merge.rs | 9 +- app/src/main/rust/src/libgit2/test_merge.rs | 169 ++++++++------------ 2 files changed, 73 insertions(+), 105 deletions(-) diff --git a/app/src/main/rust/src/libgit2/merge.rs b/app/src/main/rust/src/libgit2/merge.rs index 92f53597..1ceaa59a 100644 --- a/app/src/main/rust/src/libgit2/merge.rs +++ b/app/src/main/rust/src/libgit2/merge.rs @@ -28,7 +28,7 @@ fn normal_merge( repo: &Repository, local: &git2::AnnotatedCommit, remote: &git2::AnnotatedCommit, - author: &GitAuthor + author: &GitAuthor, ) -> Result<(), git2::Error> { let local_tree = repo.find_commit(local.id())?.tree()?; let remote_tree = repo.find_commit(remote.id())?.tree()?; @@ -59,7 +59,10 @@ fn normal_merge( &[&local_commit, &remote_commit], )?; // Set working tree to match head. - repo.checkout_head(None)?; + let mut checkout_opts = git2::build::CheckoutBuilder::new(); + checkout_opts.force(); // Crucial pour que file2.txt apparaisse sur le disque + repo.checkout_head(Some(&mut checkout_opts))?; + Ok(()) } @@ -67,7 +70,7 @@ pub fn do_merge<'a>( repo: &'a Repository, remote_branch: &str, fetch_commit: git2::AnnotatedCommit<'a>, - author: &GitAuthor + author: &GitAuthor, ) -> Result<(), Error> { // 1. do a merge analysis let analysis = repo diff --git a/app/src/main/rust/src/libgit2/test_merge.rs b/app/src/main/rust/src/libgit2/test_merge.rs index 392221e2..ed1fa3dc 100644 --- a/app/src/main/rust/src/libgit2/test_merge.rs +++ b/app/src/main/rust/src/libgit2/test_merge.rs @@ -1,130 +1,95 @@ -use std::{fs, io, path::Path}; +use git2::{Commit, ObjectType, Repository, Signature, build::CheckoutBuilder}; +use std::fs; +use std::path::Path; -use git2::{AnnotatedCommit, Oid, Repository, Signature}; - -use crate::{GitAuthor, libgit2::merge::do_merge}; - -fn clear_dir>(path: P) -> io::Result<()> { - for entry in fs::read_dir(path)? { - let entry = entry?; - let path = entry.path(); - - if path.is_dir() { - fs::remove_dir_all(path)?; - } else { - fs::remove_file(path)?; - } - } - Ok(()) -} +// --- Helpers de base --- fn signature() -> Signature<'static> { Signature::now("Moi", "test@example.com").unwrap() } -fn create_initial_commit(repo: &Repository) -> Oid { - let sig = signature(); - - let tree_id = { - let mut index = repo.index().unwrap(); - index.write_tree().unwrap() - }; - - let tree = repo.find_tree(tree_id).unwrap(); - - repo.commit(Some("HEAD"), &sig, &sig, "initial commit", &tree, &[]) - .unwrap() -} - -fn create_branch(repo: &Repository, branch: &str, parent: Oid) { - let parent = repo.find_commit(parent).unwrap(); - repo.branch(branch, &parent, false).unwrap(); +/// Bascule sur une branche en mettant à jour physiquement les fichiers (Checkout) +fn switch_to_branch(repo: &Repository, branch_name: &str) { + let ref_name = format!("refs/heads/{}", branch_name); + let obj = repo.revparse_single(&ref_name).unwrap() + .peel_to_commit().unwrap(); + + let mut opts = CheckoutBuilder::new(); + opts.force(); // Écrase les reliquats pour garantir un état propre + + repo.checkout_tree(obj.as_object(), Some(&mut opts)).unwrap(); + repo.set_head(&ref_name).unwrap(); } -fn annotated_commit<'a>(repo: &'a Repository, branch: &str) -> AnnotatedCommit<'a> { - let dev_ref = repo - .find_reference(&format!("refs/heads/{branch}")) - .unwrap(); - repo.reference_to_annotated_commit(&dev_ref).unwrap() -} - -pub fn commit_file( - repo: &Repository, - path: &Path, - contents: &str, - message: &str, - branch: &str, -) -> Oid { - // Write file to working tree - let full_path = repo.workdir().unwrap().join(path); - fs::create_dir_all(full_path.parent().unwrap()).unwrap(); - fs::write(&full_path, contents).unwrap(); - - // Stage file +/// Crée un commit proprement en partant de l'état actuel de l'index +pub fn commit_current_state(repo: &Repository, message: &str) -> git2::Oid { + let sig = signature(); let mut index = repo.index().unwrap(); - index.add_path(path).unwrap(); let tree_id = index.write_tree().unwrap(); let tree = repo.find_tree(tree_id).unwrap(); - // Signature - let sig = signature(); + // Récupère le parent actuel (HEAD) + let parent = repo.head().ok() + .and_then(|h| h.target()) + .and_then(|id| repo.find_commit(id).ok()); - // Parent commit (if any) - let parents = match repo.find_reference(&format!("refs/heads/{branch}")) { - Ok(r) => vec![repo.find_commit(r.target().unwrap()).unwrap()], - Err(_) => vec![], + let parents = match &parent { + Some(c) => vec![c], + None => vec![], }; - let parent_refs: Vec<&git2::Commit> = parents.iter().collect(); - - // Commit - let oid = repo - .commit( - Some(&format!("refs/heads/{branch}")), - &sig, - &sig, - message, - &tree, - &parent_refs, - ) - .unwrap(); - - oid + repo.commit(Some("HEAD"), &sig, &sig, message, &tree, &parents).unwrap() } -fn set_head(repo: &Repository, branch: &str) { - repo.set_head(&format!("refs/heads/{branch}")).unwrap(); - repo.checkout_head(None).unwrap(); +/// Ajoute un fichier physiquement ET dans l'index +fn add_file(repo: &Repository, filename: &str, content: &str) { + let path = repo.workdir().unwrap().join(filename); + fs::write(path, content).unwrap(); + + let mut index = repo.index().unwrap(); + index.add_path(Path::new(filename)).unwrap(); + index.write().unwrap(); } -#[test] -fn do_test() { - let path = "repo_test/repo1"; - - let _ = clear_dir(path); - - let _ = fs::create_dir_all(path); +// --- Le Test --- +#[test] +fn test_clean_merge_flow() { + let path = "repo_test/clean_repo"; + let _ = fs::remove_dir_all(path); // Nettoyage initial let repo = Repository::init(path).unwrap(); - let oid1 = commit_file(&repo, Path::new("file1.txt"), "hello", "file 1", "master"); + // 1. Premier commit sur Master + add_file(&repo, "file1.txt", "Contenu Initial\n"); + let oid1 = commit_current_state(&repo, "Initial commit on master"); - create_branch(&repo, "dev", oid1); + // 2. Créer et passer sur la branche 'dev' + let commit1 = repo.find_commit(oid1).unwrap(); + repo.branch("dev", &commit1, false).unwrap(); + switch_to_branch(&repo, "dev"); - let _oid2 = commit_file( - &repo, - Path::new("file1.txt"), - "hello world", - "update file 1", - "master", - ); + // 3. Commit sur 'dev' (file2.txt) + add_file(&repo, "file2.txt", "Contenu Dev\n"); + commit_current_state(&repo, "Add file2 on dev"); - let _oid3 = commit_file(&repo, Path::new("file2.txt"), "hello", "file 2", "dev"); + // 4. Retour sur 'master' et commit (file3.txt) + switch_to_branch(&repo, "master"); + add_file(&repo, "file3.txt", "Contenu Master\n"); + commit_current_state(&repo, "Add file3 on master"); - let author = GitAuthor::from(signature()); + // 5. Merge 'dev' dans 'master' + let annotated_dev = { + let dev_ref = repo.find_reference("refs/heads/dev").unwrap(); + repo.reference_to_annotated_commit(&dev_ref).unwrap() + }; - let annotated = annotated_commit(&repo, "dev"); + // Ici on appelle votre fonction do_merge + // Note: Le merge devrait être "Fast-forward" ou "Clean" car les fichiers sont différents + let author = crate::GitAuthor::from(signature()); + crate::libgit2::merge::do_merge(&repo, "dev", annotated_dev, &author).expect("Merge failed"); - set_head(&repo, "master"); - do_merge(&repo, "dev", annotated, &author).unwrap(); -} + // Vérification finale + assert!(repo.workdir().unwrap().join("file1.txt").exists()); + assert!(repo.workdir().unwrap().join("file2.txt").exists()); + assert!(repo.workdir().unwrap().join("file3.txt").exists()); +} \ No newline at end of file From 21bf5315bfb0acf2de7a9232508661b2b18a1759 Mon Sep 17 00:00:00 2001 From: wiiznokes <78230769+wiiznokes@users.noreply.github.com> Date: Tue, 6 Jan 2026 15:14:37 +0100 Subject: [PATCH 4/5] Update test_merge.rs --- app/src/main/rust/src/libgit2/test_merge.rs | 119 +++++++++++++++----- 1 file changed, 89 insertions(+), 30 deletions(-) diff --git a/app/src/main/rust/src/libgit2/test_merge.rs b/app/src/main/rust/src/libgit2/test_merge.rs index ed1fa3dc..770c6958 100644 --- a/app/src/main/rust/src/libgit2/test_merge.rs +++ b/app/src/main/rust/src/libgit2/test_merge.rs @@ -1,27 +1,44 @@ -use git2::{Commit, ObjectType, Repository, Signature, build::CheckoutBuilder}; -use std::fs; +use git2::{Repository, Signature, build::CheckoutBuilder}; use std::path::Path; - -// --- Helpers de base --- +use std::{fs, io}; + +use crate::GitAuthor; +use crate::libgit2::merge::do_merge; + +fn clear_dir>(path: P) -> io::Result<()> { + for entry in fs::read_dir(path)? { + let entry = entry?; + let path = entry.path(); + + if path.is_dir() { + fs::remove_dir_all(path)?; + } else { + fs::remove_file(path)?; + } + } + Ok(()) +} fn signature() -> Signature<'static> { Signature::now("Moi", "test@example.com").unwrap() } -/// Bascule sur une branche en mettant à jour physiquement les fichiers (Checkout) fn switch_to_branch(repo: &Repository, branch_name: &str) { let ref_name = format!("refs/heads/{}", branch_name); - let obj = repo.revparse_single(&ref_name).unwrap() - .peel_to_commit().unwrap(); - + let obj = repo + .revparse_single(&ref_name) + .unwrap() + .peel_to_commit() + .unwrap(); + let mut opts = CheckoutBuilder::new(); - opts.force(); // Écrase les reliquats pour garantir un état propre - - repo.checkout_tree(obj.as_object(), Some(&mut opts)).unwrap(); + opts.force(); + + repo.checkout_tree(obj.as_object(), Some(&mut opts)) + .unwrap(); repo.set_head(&ref_name).unwrap(); } -/// Crée un commit proprement en partant de l'état actuel de l'index pub fn commit_current_state(repo: &Repository, message: &str) -> git2::Oid { let sig = signature(); let mut index = repo.index().unwrap(); @@ -29,7 +46,9 @@ pub fn commit_current_state(repo: &Repository, message: &str) -> git2::Oid { let tree = repo.find_tree(tree_id).unwrap(); // Récupère le parent actuel (HEAD) - let parent = repo.head().ok() + let parent = repo + .head() + .ok() .and_then(|h| h.target()) .and_then(|id| repo.find_commit(id).ok()); @@ -38,29 +57,35 @@ pub fn commit_current_state(repo: &Repository, message: &str) -> git2::Oid { None => vec![], }; - repo.commit(Some("HEAD"), &sig, &sig, message, &tree, &parents).unwrap() + repo.commit(Some("HEAD"), &sig, &sig, message, &tree, &parents) + .unwrap() } -/// Ajoute un fichier physiquement ET dans l'index fn add_file(repo: &Repository, filename: &str, content: &str) { let path = repo.workdir().unwrap().join(filename); fs::write(path, content).unwrap(); - + let mut index = repo.index().unwrap(); index.add_path(Path::new(filename)).unwrap(); index.write().unwrap(); } -// --- Le Test --- +fn assert_content(repo: &Repository, path: &str, content: &str) { + let path = repo.workdir().unwrap().join(path); + + let real_content = fs::read_to_string(&path).unwrap(); + + assert_eq!(real_content, content); +} #[test] fn test_clean_merge_flow() { let path = "repo_test/clean_repo"; - let _ = fs::remove_dir_all(path); // Nettoyage initial + let _ = clear_dir(path); let repo = Repository::init(path).unwrap(); // 1. Premier commit sur Master - add_file(&repo, "file1.txt", "Contenu Initial\n"); + add_file(&repo, "file1.txt", "hello"); let oid1 = commit_current_state(&repo, "Initial commit on master"); // 2. Créer et passer sur la branche 'dev' @@ -69,12 +94,49 @@ fn test_clean_merge_flow() { switch_to_branch(&repo, "dev"); // 3. Commit sur 'dev' (file2.txt) - add_file(&repo, "file2.txt", "Contenu Dev\n"); + add_file(&repo, "file2.txt", "hello"); commit_current_state(&repo, "Add file2 on dev"); // 4. Retour sur 'master' et commit (file3.txt) switch_to_branch(&repo, "master"); - add_file(&repo, "file3.txt", "Contenu Master\n"); + add_file(&repo, "file1.txt", "hello world"); + commit_current_state(&repo, "Modif file1 on master"); + + // 5. Merge 'dev' dans 'master' + let annotated_dev = { + let dev_ref = repo.find_reference("refs/heads/dev").unwrap(); + repo.reference_to_annotated_commit(&dev_ref).unwrap() + }; + + let author = GitAuthor::from(signature()); + do_merge(&repo, "dev", annotated_dev, &author).expect("Merge failed"); + + assert_content(&repo, "file1.txt", "hello world"); + assert_content(&repo, "file2.txt", "hello"); +} + +#[test] +fn test_clean_merge_flow2() { + let path = "repo_test/clean_repo2"; + let _ = clear_dir(path); + let repo = Repository::init(path).unwrap(); + + // 1. Premier commit sur Master + add_file(&repo, "file1.txt", "Contenu Initial"); + let oid1 = commit_current_state(&repo, "Initial commit on master"); + + // 2. Créer et passer sur la branche 'dev' + let commit1 = repo.find_commit(oid1).unwrap(); + repo.branch("dev", &commit1, false).unwrap(); + switch_to_branch(&repo, "dev"); + + // 3. Commit sur 'dev' (file2.txt) + add_file(&repo, "file2.txt", "Contenu Dev"); + commit_current_state(&repo, "Add file2 on dev"); + + // 4. Retour sur 'master' et commit (file3.txt) + switch_to_branch(&repo, "master"); + add_file(&repo, "file3.txt", "Contenu Master"); commit_current_state(&repo, "Add file3 on master"); // 5. Merge 'dev' dans 'master' @@ -83,13 +145,10 @@ fn test_clean_merge_flow() { repo.reference_to_annotated_commit(&dev_ref).unwrap() }; - // Ici on appelle votre fonction do_merge - // Note: Le merge devrait être "Fast-forward" ou "Clean" car les fichiers sont différents - let author = crate::GitAuthor::from(signature()); - crate::libgit2::merge::do_merge(&repo, "dev", annotated_dev, &author).expect("Merge failed"); + let author = GitAuthor::from(signature()); + do_merge(&repo, "dev", annotated_dev, &author).expect("Merge failed"); - // Vérification finale - assert!(repo.workdir().unwrap().join("file1.txt").exists()); - assert!(repo.workdir().unwrap().join("file2.txt").exists()); - assert!(repo.workdir().unwrap().join("file3.txt").exists()); -} \ No newline at end of file + assert_content(&repo, "file1.txt", "Contenu Initial"); + assert_content(&repo, "file2.txt", "Contenu Dev"); + assert_content(&repo, "file3.txt", "Contenu Master"); +} From 70e39cbcc1c0f9d1392a6c04e0ae6bdef9722e24 Mon Sep 17 00:00:00 2001 From: wiiznokes <78230769+wiiznokes@users.noreply.github.com> Date: Tue, 6 Jan 2026 15:31:28 +0100 Subject: [PATCH 5/5] Update merge.rs --- app/src/main/rust/src/libgit2/merge.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/rust/src/libgit2/merge.rs b/app/src/main/rust/src/libgit2/merge.rs index 1ceaa59a..3d6511dc 100644 --- a/app/src/main/rust/src/libgit2/merge.rs +++ b/app/src/main/rust/src/libgit2/merge.rs @@ -60,7 +60,7 @@ fn normal_merge( )?; // Set working tree to match head. let mut checkout_opts = git2::build::CheckoutBuilder::new(); - checkout_opts.force(); // Crucial pour que file2.txt apparaisse sur le disque + checkout_opts.force(); repo.checkout_head(Some(&mut checkout_opts))?; Ok(())