如何使用 Rust git2 crate 执行 "git pull" 操作?

6

git2框架没有直接执行“git pull”操作的方法。

我看到了这个问题,并尝试以同样的方式执行(playground):

use std::fs;
use std::fs::File;
use std::io::{stderr, stdout, Write};
use std::path::Path;

use git2::{Commit, Error, Index, MergeOptions, ObjectType, Repository, ResetType};

struct Repo {
    url: &'static str,
    path: &'static str,
    branch: &'static str,
}

impl Repo {
    fn reset(&self, path: &Path) {
        let repo = match Repository::open(path) {
            Ok(repo) => repo,
            Err(e) => panic!("Failed to open: {}", e),
        };
        repo.reset(
            &repo.revparse_single("HEAD").unwrap(),
            ResetType::Hard,
            None,
        )
        .unwrap();
    }

    fn clone(&self) {
        let repo = match Repository::clone(self.url, self.path) {
            Ok(repo) => repo,
            Err(e) => panic!("failed to init: {}", e),
        };
    }

    fn find_last_commit<'repo>(&self, repo: &'repo Repository) -> Result<Commit<'repo>, Error> {
        let obj = repo.head()?.resolve()?.peel(ObjectType::Commit)?;
        match obj.into_commit() {
            Ok(c) => Ok(c),
            _ => Err(Error::from_str("commit error")),
        }
    }

    fn pull(&self, path: &Path) -> Result<Index, Error> {
        let repo = Repository::open(path)?;

        repo.find_remote("origin")?
            .fetch(&[self.branch], None, None)?;

        let last_commit = self.find_last_commit(&repo)?;
        let reference = repo.find_reference("FETCH_HEAD")?;
        let fetched_commit = reference.peel_to_commit()?;
        let index =
            repo.merge_commits(&last_commit, &fetched_commit, Some(&MergeOptions::new()))?;

        return Ok(index);
    }

    pub fn check(&self) {
        let repo_path = Path::new(self.path);

        if !repo_path.exists() {
            self.clone();
            return;
        }

        if repo_path.exists() && repo_path.is_dir() {
            self.reset(repo_path);
            let idx = match self.pull(repo_path) {
                Ok(idx) => idx,
                Err(e) => panic!("Failed to pull: {}", e),
            };
        }
    }
}

fn main() {
    let currencies = Repo {
        url: "https://github.com/datasets/currency-codes",
        path: "./resources/currency-codes",
        branch: "master",
    };

    currencies.check();
}

尽管clonereset能够使用,但看起来pull不能。

我做错了什么?


1
你能清楚地说明“不起作用”是什么意思吗?你期望发生什么,实际上又发生了什么? - Shepmaster
我希望从外部存储库中的更改会被拉取到本地存储库中。就好像我在控制台中键入“git pull”一样。但是在运行此代码后,本地存储库中没有任何更改。 - Argentumbolo
它确实有效,不是吗? - edwardw
@edwardw 我在两个系统Win10和Ubuntu 18.10上尝试了一下。步骤如下: 1)构建一次并获取repo克隆。 2)在repo中执行“git reset --hard HEAD^^^”。检查HEAD是否更改。 3)再次运行“cargo build”。 4)检查结果:HEAD仍处于相同的位置。 5)通过手动运行“git pull”再次确认 - HEAD与#1后的位置相同。因此,这段代码和git pull肯定有不同的方式。 - Argentumbolo
@Argentumbolo "cargo build" 不会运行任何东西,请尝试使用 "cargo run"。 - Jussi Kukkonen
1
@JussiKukkonen 我忘了提到 - 这段代码是 "build.rs" 的一部分。所以它在 "cargo build" 时起作用。 - Argentumbolo
1个回答

6
< p > git2-rs 仓库确实有一个未处理的PR,其中包含一个拉取请求示例。我在这里稍作修改,以展示如何进行快速转发,因为这正是你要寻找的:

fn fast_forward(&self, path: &Path) -> Result<(), Error> {
    let repo = Repository::open(path)?;

    repo.find_remote("origin")?
        .fetch(&[self.branch], None, None)?;

    let fetch_head = repo.find_reference("FETCH_HEAD")?;
    let fetch_commit = repo.reference_to_annotated_commit(&fetch_head)?;
    let analysis = repo.merge_analysis(&[&fetch_commit])?;
    if analysis.0.is_up_to_date() {
        Ok(())
    } else if analysis.0.is_fast_forward() {
        let refname = format!("refs/heads/{}", self.branch);
        let mut reference = repo.find_reference(&refname)?;
        reference.set_target(fetch_commit.id(), "Fast-Forward")?;
        repo.set_head(&refname)?;
        repo.checkout_head(Some(git2::build::CheckoutBuilder::default().force()))
    } else {
        Err(Error::from_str("Fast-forward only!"))
    }
}

pub fn check(&self) {
    ...
    if repo_path.exists() && repo_path.is_dir() {
        self.reset(repo_path);
        if let Err(e) = self.fast_forward(repo_path) {
            panic!("Failed to pull: {}", e)
        }
    }
}

当然,这个信誉归原作者所有。您还可以检查非平凡的合并情况,即本地树脏的情况。


谢谢。我添加了一个 analysis.0.is_up_to_date() 分支,但显然不是我要找的。 - Argentumbolo
@Argentumbolo 很好的发现。我更新了代码片段以涵盖那个问题。 - edwardw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接