gnostr_asyncgit/sync/branch/
merge_ff.rs

1//! merging from upstream
2
3use scopetime::scope_time;
4
5use super::BranchType;
6use crate::{
7	error::{Error, Result},
8	sync::{RepoPath, repository::repo},
9};
10
11///
12pub fn branch_merge_upstream_fastforward(
13	repo_path: &RepoPath,
14	branch: &str,
15) -> Result<()> {
16	scope_time!("branch_merge_upstream");
17
18	let repo = repo(repo_path)?;
19
20	let branch = repo.find_branch(branch, BranchType::Local)?;
21	let upstream = branch.upstream()?;
22
23	let upstream_commit =
24		upstream.into_reference().peel_to_commit()?;
25
26	let annotated =
27		repo.find_annotated_commit(upstream_commit.id())?;
28
29	let (analysis, pref) = repo.merge_analysis(&[&annotated])?;
30
31	if !analysis.is_fast_forward() {
32		return Err(Error::Generic(
33			"fast forward merge not possible".into(),
34		));
35	}
36
37	if pref.is_no_fast_forward() {
38		return Err(Error::Generic("fast forward not wanted".into()));
39	}
40
41	//TODO: support merge on unborn
42	if analysis.is_unborn() {
43		return Err(Error::Generic("head is unborn".into()));
44	}
45
46	repo.checkout_tree(upstream_commit.as_object(), None)?;
47
48	repo.head()?.set_target(annotated.id(), "")?;
49
50	Ok(())
51}
52
53#[cfg(test)]
54pub mod test {
55	use super::*;
56	use crate::sync::{
57		remotes::{fetch, push::push_branch},
58		tests::{
59			debug_cmd_print, get_commit_ids, repo_clone,
60			repo_init_bare, write_commit_file,
61		},
62	};
63
64	#[test]
65	fn test_merge_fastforward() {
66		let (r1_dir, _repo) = repo_init_bare().unwrap();
67
68		let (clone1_dir, clone1) =
69			repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
70
71		let (clone2_dir, clone2) =
72			repo_clone(r1_dir.path().to_str().unwrap()).unwrap();
73
74		// clone1
75
76		let commit1 =
77			write_commit_file(&clone1, "test.txt", "test", "commit1");
78
79		push_branch(
80			&clone1_dir.path().to_str().unwrap().into(),
81			"origin",
82			"master",
83			false,
84			false,
85			None,
86			None,
87		)
88		.unwrap();
89
90		// clone2
91		debug_cmd_print(
92			&clone2_dir.path().to_str().unwrap().into(),
93			"git pull --ff",
94		);
95
96		let commit2 = write_commit_file(
97			&clone2,
98			"test2.txt",
99			"test",
100			"commit2",
101		);
102
103		push_branch(
104			&clone2_dir.path().to_str().unwrap().into(),
105			"origin",
106			"master",
107			false,
108			false,
109			None,
110			None,
111		)
112		.unwrap();
113
114		// clone1 again
115
116		let bytes = fetch(
117			&clone1_dir.path().to_str().unwrap().into(),
118			"master",
119			None,
120			None,
121		)
122		.unwrap();
123		assert!(bytes > 0);
124
125		let bytes = fetch(
126			&clone1_dir.path().to_str().unwrap().into(),
127			"master",
128			None,
129			None,
130		)
131		.unwrap();
132		assert_eq!(bytes, 0);
133
134		branch_merge_upstream_fastforward(
135			&clone1_dir.path().to_str().unwrap().into(),
136			"master",
137		)
138		.unwrap();
139
140		let commits = get_commit_ids(&clone1, 10);
141		assert_eq!(commits.len(), 2);
142		assert_eq!(commits[1], commit1);
143		assert_eq!(commits[0], commit2);
144	}
145}