【问题标题】:How to perform "git pull" with the Rust git2 crate?如何使用 Rust git2 crate 执行“git pull”?
【发布时间】:2019-11-08 14:55:02
【问题描述】:

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

我见过 this question 并尝试以同样的方式 (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 无效。

我做错了什么?

【问题讨论】:

  • 你能清楚地说明“不起作用”是什么意思吗?您期望会发生什么以及实际会发生什么?
  • 我希望来自外部存储库的更改将被拉到本地存储库。与我在控制台中键入“git pull”的方式完全相同。但是在我运行此代码后,本地仓库中的 nothing 根本没有改变。
  • 它确实有效,不是吗?
  • @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 的工作方式肯定不同。
  • @JussiKukkonen 我忘了提——这段代码是“build.rs”的一部分。所以它适用于“cargo build”。

标签: git rust pull libgit2


【解决方案1】:

git2-rs 存储库确实有 a pending 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() 分支,但这显然是我要找的。​​span>
  • @Argentumbolo 不错。我更新了代码 sn-p 以涵盖这一点。
猜你喜欢
  • 2019-08-04
  • 2011-09-15
  • 2015-07-08
  • 2017-01-02
  • 2012-10-16
  • 1970-01-01
  • 2011-05-31
  • 2011-07-27
  • 2016-01-07
相关资源
最近更新 更多