rust-lang / git2-rs

libgit2 bindings for Rust
https://docs.rs/git2
Apache License 2.0
1.67k stars 384 forks source link

wrong lifetime for Repository Index #897

Closed seanaye closed 1 year ago

seanaye commented 1 year ago

I am following the example from the docs for replicating git add *

my code:

    let repo = match get_marble_store() {
        Ok(r) => Ok(r),
        Err(_) => match Repository::open(get_path(None)) {
            Ok(r) => Ok(r),
            Err(e) => Err(e),
        },
    };

    let mut index = repo?.index()?;
    index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?;

    index.write()?;
    return Ok(());

When I run this I get cannot index add all. This operation is not allowed against bare repositories. This repo is not a bare repo though. in .git/config bare=false.

I have tried using a repo created with git init as well as with git2-rs

    let mut opts = RepositoryInitOptions::new();
    opts.no_reinit(true).mkdir(true).mkpath(true).bare(false);
    return Repository::init_opts(path, &opts);
ehuss commented 1 year ago

Can you show a complete example? For example, something like this:

use git2::*;
use std::error::Error;
use std::path::Path;

fn main() -> Result<(), Box<dyn Error>> {
    let repo_path = Path::new("myrepo");
    if repo_path.exists() {
        std::fs::remove_dir_all(&repo_path)?;
    }
    let mut opts = RepositoryInitOptions::new();
    opts.no_reinit(true).mkdir(true).mkpath(true).bare(false);
    let repo = Repository::init_opts(&repo_path, &opts)?;
    std::fs::write("myrepo/somefile.txt", "This is an example")?;
    let mut index = repo.index()?;
    index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?;
    index.write()?;
    Ok(())
}

The above works for me.

seanaye commented 1 year ago

@ehuss thanks for looking at this

here a full sample of relevant code

use git2::{Repository, RepositoryInitOptions};
use std::fs::{write};
use std::path::PathBuf;

fn get_path(f: Option<&str>) -> PathBuf {
    let root = "/Users/seanaye/Desktop/marble/";
    return match f {
        None => PathBuf::from(root),
        Some(p) => [root, p].iter().collect(),
    };
}

fn get_marble_store() -> Result<git2::Repository, git2::Error> {
    let path = get_path(None);
    println!("{:?}", path);
    let mut opts = RepositoryInitOptions::new();
    opts.no_reinit(true).mkdir(true).mkpath(true).bare(false);
    return Repository::init_opts(path, &opts);
}

fn commit_changes() -> Result<(), git2::Error> {
    let repo = match get_marble_store() {
        Ok(r) => Ok(r),
        Err(_) => match Repository::open(get_path(None)) {
            Ok(r) => Ok(r),
            Err(e) => Err(e),
        },
    };

    let mut index = repo?.index()?;
    index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?;

    index.write()?;
    return Ok(());
}

#[tauri::command]
fn save(filename: &str, data: &str) {
    println!("{:?}", data);
    let filepath = get_path(Some(filename));
    let res = write(filepath, data);
    if res.is_err() {
        println!("{:?}", res.unwrap_err())
    }

    match commit_changes() {
        Ok(_) => println!("Changes saved!"),
        Err(e) => println!("Error saving changes {:?}", e)
    }

}

fn save is the "entrypoint"

seanaye commented 1 year ago

I copied over your code and it is working, but I'm not sure why mine is giving an error. It could be there is something wrong with how i'm using the library but I don't think the error message should be This operation is not allowed against bare repositories

ehuss commented 1 year ago

The Repository is being dropped in the line let mut index = repo?.index()?;. To work around that, place the repo in a local variable, as in:

    let repo = repo?;
    let mut index = repo.index()?;

Looking at the code, I think the bindings need to add some lifetimes to ensure an "owner" like the Repository outlives the things associated with it like the Index.