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