gnostr_asyncgit/sync/
merge.rs

1use git2::{BranchType, Commit, MergeOptions, Repository};
2use scopetime::scope_time;
3
4use super::{
5    rebase::{RebaseProgress, RebaseState},
6    RepoPath,
7};
8use crate::{
9    error::{Error, Result},
10    sync::{
11        branch::merge_commit::commit_merge_with_head,
12        rebase::{abort_rebase, continue_rebase, get_rebase_progress},
13        repository::repo,
14        reset_stage, reset_workdir, CommitId,
15    },
16};
17
18///
19pub fn mergehead_ids(repo_path: &RepoPath) -> Result<Vec<CommitId>> {
20    scope_time!("mergehead_ids");
21
22    let mut repo = repo(repo_path)?;
23
24    let mut ids: Vec<CommitId> = Vec::new();
25    repo.mergehead_foreach(|id| {
26        ids.push(CommitId::from(*id));
27        true
28    })?;
29
30    Ok(ids)
31}
32
33/// does these steps:
34/// * reset all staged changes,
35/// * revert all changes in workdir
36/// * cleanup repo merge state
37pub fn abort_pending_state(repo_path: &RepoPath) -> Result<()> {
38    scope_time!("abort_pending_state");
39
40    let repo = repo(repo_path)?;
41
42    reset_stage(repo_path, "*")?;
43    reset_workdir(repo_path, "*")?;
44
45    repo.cleanup_state()?;
46
47    Ok(())
48}
49
50///
51pub fn merge_branch(repo_path: &RepoPath, branch: &str, branch_type: BranchType) -> Result<()> {
52    scope_time!("merge_branch");
53
54    let repo = repo(repo_path)?;
55
56    merge_branch_repo(&repo, branch, branch_type)?;
57
58    Ok(())
59}
60
61///
62pub fn rebase_progress(repo_path: &RepoPath) -> Result<RebaseProgress> {
63    scope_time!("rebase_progress");
64
65    let repo = repo(repo_path)?;
66
67    get_rebase_progress(&repo)
68}
69
70///
71pub fn continue_pending_rebase(repo_path: &RepoPath) -> Result<RebaseState> {
72    scope_time!("continue_pending_rebase");
73
74    let repo = repo(repo_path)?;
75
76    continue_rebase(&repo)
77}
78
79///
80pub fn abort_pending_rebase(repo_path: &RepoPath) -> Result<()> {
81    scope_time!("abort_pending_rebase");
82
83    let repo = repo(repo_path)?;
84
85    abort_rebase(&repo)
86}
87
88///
89pub fn merge_branch_repo(repo: &Repository, branch: &str, branch_type: BranchType) -> Result<()> {
90    let branch = repo.find_branch(branch, branch_type)?;
91
92    let annotated = repo.reference_to_annotated_commit(&branch.into_reference())?;
93
94    let (analysis, _) = repo.merge_analysis(&[&annotated])?;
95
96    //TODO: support merge on unborn
97    if analysis.is_unborn() {
98        return Err(Error::Generic("head is unborn".into()));
99    }
100
101    let mut opt = MergeOptions::default();
102
103    repo.merge(&[&annotated], Some(&mut opt), None)?;
104
105    Ok(())
106}
107
108///
109pub fn merge_msg(repo_path: &RepoPath) -> Result<String> {
110    scope_time!("merge_msg");
111
112    let repo = repo(repo_path)?;
113    let content = repo.message()?;
114
115    Ok(content)
116}
117
118///
119pub fn merge_commit(repo_path: &RepoPath, msg: &str, ids: &[CommitId]) -> Result<CommitId> {
120    scope_time!("merge_commit");
121
122    let repo = repo(repo_path)?;
123
124    let mut commits: Vec<Commit> = Vec::new();
125
126    for id in ids {
127        commits.push(repo.find_commit((*id).into())?);
128    }
129
130    let id = commit_merge_with_head(&repo, &commits, msg)?;
131
132    Ok(id)
133}
134
135#[cfg(test)]
136mod tests {
137    use pretty_assertions::assert_eq;
138
139    use super::*;
140    use crate::sync::{
141        create_branch,
142        tests::{repo_init, write_commit_file},
143        RepoPath,
144    };
145
146    #[test]
147    fn test_smoke() {
148        let (_td, repo) = repo_init().unwrap();
149        let root = repo.path().parent().unwrap();
150        let repo_path: &RepoPath = &root.as_os_str().to_str().unwrap().into();
151
152        let c1 = write_commit_file(&repo, "test.txt", "test", "commit1");
153
154        create_branch(repo_path, "foo").unwrap();
155
156        write_commit_file(&repo, "test.txt", "test2", "commit2");
157
158        merge_branch(repo_path, "master", BranchType::Local).unwrap();
159
160        let msg = merge_msg(repo_path).unwrap();
161
162        assert_eq!(&msg[0..12], "Merge branch");
163
164        let mergeheads = mergehead_ids(repo_path).unwrap();
165
166        assert_eq!(mergeheads[0], c1);
167    }
168}