git_api/
branch.rs

1use git2::{build::CheckoutBuilder, Error, Repository};
2use macro_log::*;
3
4/// (强制)切换到远程分支(switch -f)
5///
6/// 不会创建本地分支,HEAD将是分离的
7///
8/// 缺点:
9/// - 分离状态下`repo.head().name()`的值是"HEAD",查询不到分支名
10/// - 两个commit相同的远程分支之间无法切换
11pub fn repo_switch(
12    repo: &Repository,
13    remote_name: &str,
14    branch_name: &str,
15    force_checkout: bool,
16) -> Result<(), Error> {
17    // 分离HEAD
18    repo.set_head(&format!("refs/remotes/{remote_name}/{branch_name}"))?;
19    // 强制checkout
20    let mut opts = CheckoutBuilder::new();
21    if force_checkout {
22        opts.force();
23    }
24    repo.checkout_head(Some(&mut opts))?;
25    Ok(())
26}
27
28/// (强制)检出远程分支(checkout -f)
29///
30/// 将创建本地分支,如果已存在,则先删除再更新
31pub fn repo_checkout(
32    repo: &Repository,
33    remote_name: &str,
34    branch_name: &str,
35    force_checkout: bool,
36) -> Result<(), Error> {
37    let obj = repo.revparse_single(&format!("refs/remotes/{remote_name}/{branch_name}"))?;
38    let Some(commit) = obj.as_commit() else {
39        return Err(Error::from_str("Failed revparse_single commit"));
40    };
41
42    // 分离HEAD
43    repo.set_head(&format!("refs/remotes/{remote_name}/{branch_name}"))?;
44    // 删除本地已有的同名分支
45    if let Ok(mut branch) = repo.find_branch(branch_name, git2::BranchType::Local) {
46        w!("分支 {branch_name} 已存在,更新");
47        branch.delete()?;
48    }
49    // 创建分支
50    let _branch = repo.branch(branch_name, commit, false)?;
51    // 切换分支
52    repo.set_head(&format!("refs/heads/{branch_name}"))?;
53
54    // 强制checkout
55    let mut opts = CheckoutBuilder::new();
56    if force_checkout {
57        opts.force();
58    }
59    repo.checkout_head(Some(&mut opts))?;
60
61    Ok(())
62}