gnostr_asyncgit/sync/branch/
merge_rebase.rs1use git2::BranchType;
4use scopetime::scope_time;
5
6use crate::{
7 error::{Error, Result},
8 sync::{rebase::conflict_free_rebase, repository::repo, CommitId, RepoPath},
9};
10
11pub fn merge_upstream_rebase(repo_path: &RepoPath, branch_name: &str) -> Result<CommitId> {
13 scope_time!("merge_upstream_rebase");
14
15 let repo = repo(repo_path)?;
16 if super::get_branch_name_repo(&repo)? != branch_name {
17 return Err(Error::Generic(String::from(
18 "can only rebase in head branch",
19 )));
20 }
21
22 let branch = repo.find_branch(branch_name, BranchType::Local)?;
23 let upstream = branch.upstream()?;
24 let upstream_commit = upstream.get().peel_to_commit()?;
25 let annotated_upstream = repo.find_annotated_commit(upstream_commit.id())?;
26
27 conflict_free_rebase(&repo, &annotated_upstream)
28}
29
30#[cfg(test)]
31mod test {
32 use git2::{Repository, Time};
33
34 use super::*;
35 use crate::sync::{
36 branch_compare_upstream, get_commits_info,
37 remotes::{fetch, push::push_branch},
38 tests::{
39 debug_cmd_print, get_commit_ids, repo_clone, repo_init_bare, write_commit_file,
40 write_commit_file_at,
41 },
42 RepoState,
43 };
44
45 fn get_commit_msgs(r: &Repository) -> Vec<String> {
46 let commits = get_commit_ids(r, 10);
47 get_commits_info(&r.workdir().unwrap().to_str().unwrap().into(), &commits, 10)
48 .unwrap()
49 .into_iter()
50 .map(|c| c.message)
51 .collect()
52 }
53
54 #[test]
55 fn test_merge_normal() {
56 let (r1_dir, _repo) = repo_init_bare().unwrap();
57
58 let (clone1_dir, clone1) = repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
59
60 let clone1_dir = clone1_dir.path().to_str().unwrap();
61
62 let _commit1 = write_commit_file_at(
65 &clone1,
66 "test.txt",
67 "test",
68 "commit1",
69 git2::Time::new(0, 0),
70 );
71
72 assert_eq!(clone1.head_detached().unwrap(), false);
73
74 push_branch(
75 &clone1_dir.into(),
76 "origin",
77 "master",
78 false,
79 false,
80 None,
81 None,
82 )
83 .unwrap();
84
85 assert_eq!(clone1.head_detached().unwrap(), false);
86
87 let (clone2_dir, clone2) = repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
90
91 let clone2_dir = clone2_dir.path().to_str().unwrap();
92
93 let _commit2 = write_commit_file_at(
94 &clone2,
95 "test2.txt",
96 "test",
97 "commit2",
98 git2::Time::new(1, 0),
99 );
100
101 assert_eq!(clone2.head_detached().unwrap(), false);
102
103 push_branch(
104 &clone2_dir.into(),
105 "origin",
106 "master",
107 false,
108 false,
109 None,
110 None,
111 )
112 .unwrap();
113
114 assert_eq!(clone2.head_detached().unwrap(), false);
115
116 let _commit3 = write_commit_file_at(
119 &clone1,
120 "test3.txt",
121 "test",
122 "commit3",
123 git2::Time::new(2, 0),
124 );
125
126 assert_eq!(clone1.head_detached().unwrap(), false);
127
128 let bytes = fetch(&clone1_dir.into(), "master", None, None).unwrap();
130 assert!(bytes > 0);
131
132 assert_eq!(
134 branch_compare_upstream(&clone1_dir.into(), "master")
135 .unwrap()
136 .behind,
137 1
138 );
139
140 assert_eq!(clone1.head_detached().unwrap(), false);
143
144 merge_upstream_rebase(&clone1_dir.into(), "master").unwrap();
145
146 debug_cmd_print(&clone1_dir.into(), "git log");
147
148 let state = crate::sync::repo_state(&clone1_dir.into()).unwrap();
149 assert_eq!(state, RepoState::Clean);
150
151 let commits = get_commit_msgs(&clone1);
152 assert_eq!(
153 commits,
154 vec![
155 String::from("commit3"),
156 String::from("commit2"),
157 String::from("commit1")
158 ]
159 );
160
161 assert_eq!(clone1.head_detached().unwrap(), false);
162 }
163
164 #[test]
165 fn test_merge_multiple() {
166 let (r1_dir, _repo) = repo_init_bare().unwrap();
167
168 let (clone1_dir, clone1) = repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
169
170 let clone1_dir = clone1_dir.path().to_str().unwrap();
171
172 write_commit_file_at(&clone1, "test.txt", "test", "commit1", Time::new(0, 0));
175
176 push_branch(
177 &clone1_dir.into(),
178 "origin",
179 "master",
180 false,
181 false,
182 None,
183 None,
184 )
185 .unwrap();
186
187 let (clone2_dir, clone2) = repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
190
191 let clone2_dir = clone2_dir.path().to_str().unwrap();
192
193 write_commit_file_at(&clone2, "test2.txt", "test", "commit2", Time::new(1, 0));
194
195 push_branch(
196 &clone2_dir.into(),
197 "origin",
198 "master",
199 false,
200 false,
201 None,
202 None,
203 )
204 .unwrap();
205
206 write_commit_file_at(&clone1, "test3.txt", "test", "commit3", Time::new(2, 0));
209 write_commit_file_at(&clone1, "test4.txt", "test", "commit4", Time::new(3, 0));
210
211 fetch(&clone1_dir.into(), "master", None, None).unwrap();
214
215 merge_upstream_rebase(&clone1_dir.into(), "master").unwrap();
216
217 debug_cmd_print(&clone1_dir.into(), "git log");
218
219 let state = crate::sync::repo_state(&clone1_dir.into()).unwrap();
220 assert_eq!(state, RepoState::Clean);
221
222 let commits = get_commit_msgs(&clone1);
223 assert_eq!(
224 commits,
225 vec![
226 String::from("commit4"),
227 String::from("commit3"),
228 String::from("commit2"),
229 String::from("commit1")
230 ]
231 );
232
233 assert_eq!(clone1.head_detached().unwrap(), false);
234 }
235
236 #[test]
237 fn test_merge_conflict() {
238 let (r1_dir, _repo) = repo_init_bare().unwrap();
239
240 let (clone1_dir, clone1) = repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
241
242 let clone1_dir = clone1_dir.path().to_str().unwrap();
243
244 let _commit1 = write_commit_file(&clone1, "test.txt", "test", "commit1");
247
248 push_branch(
249 &clone1_dir.into(),
250 "origin",
251 "master",
252 false,
253 false,
254 None,
255 None,
256 )
257 .unwrap();
258
259 let (clone2_dir, clone2) = repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
262
263 let clone2_dir = clone2_dir.path().to_str().unwrap();
264
265 let _commit2 = write_commit_file(&clone2, "test2.txt", "test", "commit2");
266
267 push_branch(
268 &clone2_dir.into(),
269 "origin",
270 "master",
271 false,
272 false,
273 None,
274 None,
275 )
276 .unwrap();
277
278 let _commit3 = write_commit_file(&clone1, "test2.txt", "foo", "commit3");
281
282 let bytes = fetch(&clone1_dir.into(), "master", None, None).unwrap();
283 assert!(bytes > 0);
284
285 assert_eq!(
286 branch_compare_upstream(&clone1_dir.into(), "master")
287 .unwrap()
288 .behind,
289 1
290 );
291
292 let res = merge_upstream_rebase(&clone1_dir.into(), "master");
293 assert!(res.is_err());
294
295 let state = crate::sync::repo_state(&clone1_dir.into()).unwrap();
296
297 assert_eq!(state, RepoState::Clean);
298
299 let commits = get_commit_msgs(&clone1);
300 assert_eq!(
301 commits,
302 vec![String::from("commit3"), String::from("commit1")]
303 );
304 }
305}