|
| 1 | +//! merging from upstream (rebase) |
| 2 | +
|
| 3 | +use crate::{ |
| 4 | + error::{Error, Result}, |
| 5 | + sync::utils, |
| 6 | +}; |
| 7 | +use git2::BranchType; |
| 8 | +use scopetime::scope_time; |
| 9 | + |
| 10 | +/// trys merging current branch with its upstrema using rebase |
| 11 | +pub fn merge_upstream_rebase( |
| 12 | + repo_path: &str, |
| 13 | + branch_name: &str, |
| 14 | +) -> Result<()> { |
| 15 | + scope_time!("merge_upstream_rebase"); |
| 16 | + |
| 17 | + let repo = utils::repo(repo_path)?; |
| 18 | + let branch = repo.find_branch(branch_name, BranchType::Local)?; |
| 19 | + let upstream = branch.upstream()?; |
| 20 | + let upstream_commit = upstream.get().peel_to_commit()?; |
| 21 | + let annotated_upstream = |
| 22 | + repo.find_annotated_commit(upstream_commit.id())?; |
| 23 | + |
| 24 | + let branch_commit = branch.get().peel_to_commit()?; |
| 25 | + let annotated_branch = |
| 26 | + repo.find_annotated_commit(branch_commit.id())?; |
| 27 | + |
| 28 | + let rebase = repo.rebase( |
| 29 | + Some(&annotated_branch), |
| 30 | + Some(&annotated_upstream), |
| 31 | + None, |
| 32 | + None, |
| 33 | + )?; |
| 34 | + |
| 35 | + let signature = |
| 36 | + crate::sync::commit::signature_allow_undefined_name(&repo)?; |
| 37 | + |
| 38 | + for e in rebase { |
| 39 | + let _op = e?; |
| 40 | + // dbg!(op.id()); |
| 41 | + // dbg!(op.kind()); |
| 42 | + } |
| 43 | + |
| 44 | + let mut rebase = repo.open_rebase(None)?; |
| 45 | + |
| 46 | + if repo.index()?.has_conflicts() { |
| 47 | + rebase.abort()?; |
| 48 | + |
| 49 | + Err(Error::Generic(String::from("conflicts while merging"))) |
| 50 | + } else { |
| 51 | + rebase.commit(None, &signature, None)?; |
| 52 | + |
| 53 | + rebase.finish(Some(&signature))?; |
| 54 | + |
| 55 | + Ok(()) |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +#[cfg(test)] |
| 60 | +mod test { |
| 61 | + use super::*; |
| 62 | + use crate::sync::{ |
| 63 | + branch_compare_upstream, get_commits_info, |
| 64 | + remotes::{fetch_origin, push::push}, |
| 65 | + tests::{ |
| 66 | + debug_cmd_print, get_commit_ids, repo_clone, |
| 67 | + repo_init_bare, write_commit_file, |
| 68 | + }, |
| 69 | + RepoState, |
| 70 | + }; |
| 71 | + use git2::Repository; |
| 72 | + |
| 73 | + fn get_commit_msgs(r: &Repository) -> Vec<String> { |
| 74 | + let commits = get_commit_ids(r, 10); |
| 75 | + get_commits_info( |
| 76 | + r.workdir().unwrap().to_str().unwrap(), |
| 77 | + &commits, |
| 78 | + 10, |
| 79 | + ) |
| 80 | + .unwrap() |
| 81 | + .into_iter() |
| 82 | + .map(|c| c.message) |
| 83 | + .collect() |
| 84 | + } |
| 85 | + |
| 86 | + #[test] |
| 87 | + fn test_merge_normal() { |
| 88 | + let (r1_dir, _repo) = repo_init_bare().unwrap(); |
| 89 | + |
| 90 | + let (clone1_dir, clone1) = |
| 91 | + repo_clone(r1_dir.path().to_str().unwrap()).unwrap(); |
| 92 | + |
| 93 | + let clone1_dir = clone1_dir.path().to_str().unwrap(); |
| 94 | + |
| 95 | + // clone1 |
| 96 | + |
| 97 | + let _commit1 = |
| 98 | + write_commit_file(&clone1, "test.txt", "test", "commit1"); |
| 99 | + |
| 100 | + push(clone1_dir, "origin", "master", false, None, None) |
| 101 | + .unwrap(); |
| 102 | + |
| 103 | + // clone2 |
| 104 | + |
| 105 | + let (clone2_dir, clone2) = |
| 106 | + repo_clone(r1_dir.path().to_str().unwrap()).unwrap(); |
| 107 | + |
| 108 | + let clone2_dir = clone2_dir.path().to_str().unwrap(); |
| 109 | + |
| 110 | + let _commit2 = write_commit_file( |
| 111 | + &clone2, |
| 112 | + "test2.txt", |
| 113 | + "test", |
| 114 | + "commit2", |
| 115 | + ); |
| 116 | + |
| 117 | + push(clone2_dir, "origin", "master", false, None, None) |
| 118 | + .unwrap(); |
| 119 | + |
| 120 | + // clone1 |
| 121 | + |
| 122 | + let _commit3 = write_commit_file( |
| 123 | + &clone1, |
| 124 | + "test3.txt", |
| 125 | + "test", |
| 126 | + "commit3", |
| 127 | + ); |
| 128 | + |
| 129 | + //lets fetch from origin |
| 130 | + let bytes = |
| 131 | + fetch_origin(clone1_dir, "master", None, None).unwrap(); |
| 132 | + assert!(bytes > 0); |
| 133 | + |
| 134 | + //we should be one commit behind |
| 135 | + assert_eq!( |
| 136 | + branch_compare_upstream(clone1_dir, "master") |
| 137 | + .unwrap() |
| 138 | + .behind, |
| 139 | + 1 |
| 140 | + ); |
| 141 | + |
| 142 | + // debug_cmd_print(clone1_dir, "git log"); |
| 143 | + |
| 144 | + merge_upstream_rebase(clone1_dir, "master").unwrap(); |
| 145 | + |
| 146 | + debug_cmd_print(clone1_dir, "git log"); |
| 147 | + |
| 148 | + let state = crate::sync::repo_state(clone1_dir).unwrap(); |
| 149 | + |
| 150 | + assert_eq!(state, RepoState::Clean); |
| 151 | + |
| 152 | + let commits = get_commit_msgs(&clone1); |
| 153 | + assert_eq!( |
| 154 | + commits, |
| 155 | + vec![ |
| 156 | + String::from("commit3"), |
| 157 | + String::from("commit2"), |
| 158 | + String::from("commit1") |
| 159 | + ] |
| 160 | + ); |
| 161 | + } |
| 162 | + |
| 163 | + #[test] |
| 164 | + fn test_merge_conflict() { |
| 165 | + let (r1_dir, _repo) = repo_init_bare().unwrap(); |
| 166 | + |
| 167 | + let (clone1_dir, clone1) = |
| 168 | + repo_clone(r1_dir.path().to_str().unwrap()).unwrap(); |
| 169 | + |
| 170 | + let clone1_dir = clone1_dir.path().to_str().unwrap(); |
| 171 | + |
| 172 | + // clone1 |
| 173 | + |
| 174 | + let _commit1 = |
| 175 | + write_commit_file(&clone1, "test.txt", "test", "commit1"); |
| 176 | + |
| 177 | + push(clone1_dir, "origin", "master", false, None, None) |
| 178 | + .unwrap(); |
| 179 | + |
| 180 | + // clone2 |
| 181 | + |
| 182 | + let (clone2_dir, clone2) = |
| 183 | + repo_clone(r1_dir.path().to_str().unwrap()).unwrap(); |
| 184 | + |
| 185 | + let clone2_dir = clone2_dir.path().to_str().unwrap(); |
| 186 | + |
| 187 | + let _commit2 = write_commit_file( |
| 188 | + &clone2, |
| 189 | + "test2.txt", |
| 190 | + "test", |
| 191 | + "commit2", |
| 192 | + ); |
| 193 | + |
| 194 | + push(clone2_dir, "origin", "master", false, None, None) |
| 195 | + .unwrap(); |
| 196 | + |
| 197 | + // clone1 |
| 198 | + |
| 199 | + let _commit3 = |
| 200 | + write_commit_file(&clone1, "test2.txt", "foo", "commit3"); |
| 201 | + |
| 202 | + let bytes = |
| 203 | + fetch_origin(clone1_dir, "master", None, None).unwrap(); |
| 204 | + assert!(bytes > 0); |
| 205 | + |
| 206 | + assert_eq!( |
| 207 | + branch_compare_upstream(clone1_dir, "master") |
| 208 | + .unwrap() |
| 209 | + .behind, |
| 210 | + 1 |
| 211 | + ); |
| 212 | + |
| 213 | + let res = merge_upstream_rebase(clone1_dir, "master"); |
| 214 | + assert!(res.is_err()); |
| 215 | + |
| 216 | + let state = crate::sync::repo_state(clone1_dir).unwrap(); |
| 217 | + |
| 218 | + assert_eq!(state, RepoState::Clean); |
| 219 | + |
| 220 | + let commits = get_commit_msgs(&clone1); |
| 221 | + assert_eq!( |
| 222 | + commits, |
| 223 | + vec![String::from("commit3"), String::from("commit1")] |
| 224 | + ); |
| 225 | + } |
| 226 | +} |
0 commit comments