rust-lang / rust-lang/git2-rs

Write vs write_tree

Open
#630 3 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
2.1k
Forks
450
Avg merge
11m
Merged PRs (30d)
1

Description

I am currently using git2 0.13.12.

If i read the documentation for write: https://docs.rs/git2/0.13.12/git2/struct.Index.html#method.write

Write an existing index object from memory back to disk using an atomic file lock.

and write_tree: https://docs.rs/git2/0.13.12/git2/struct.Index.html#method.write_tree

Write the index as a tree.

This method will scan the index and write a representation of its current state back to disk; it recursively creates tree objects for each of the subtrees stored in the index, but only returns the OID of the root tree. This is the OID that can be used e.g. to create a commit.

The index instance cannot be bare, and needs to be associated to an existing repository.

The index must not contain any file in conflict.

it sounds like i can just call write_tree, get the id and then commit. That is also how the init example explains it: https://github.com/rust-lang/git2-rs/blob/master/examples/init.rs:

/// Unlike regular "git init", this example shows how to create an initial empty
/// commit in the repository. This is the helper function that does that.
fn create_initial_commit(repo: &Repository) -> Result<(), Error> {
    // First use the config to initialize a commit signature for the user.
    let sig = repo.signature()?;

    // Now let's create an empty tree for this commit
    let tree_id = {
        let mut index = repo.index()?;

        // Outside of this example, you could call index.add_path()
        // here to put actual files into the index. For our purposes, we'll
        // leave it empty for now.

        index.write_tree()?
    };

    let tree = repo.find_tree(tree_id)?;

    // Ready to create the initial commit.
    //
    // Normally creating a commit would involve looking up the current HEAD
    // commit and making that be the parent of the initial commit, but here this
    // is the first commit so there will be no parent.
    repo.commit(Some("HEAD"), &sig, &sig, "Initial commit", &tree, &[])?;

    Ok(())
}

But i find that this only work on initial commit.


I have this example
use git2::Commit;
use git2::ObjectType;
use git2::Repository;
use std::env;
use std::fs::File;
use std::path::Path;

fn find_last_commit(repo: &Repository) -> Result<Commit, git2::Error> {
    let obj = repo.head()?.resolve()?.peel(ObjectType::Commit)?;
    obj.into_commit()
        .map_err(|_| git2::Error::from_str("Couldn't find commit"))
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let root = &args[1];
    let file_name = &args[2];
    let repo = if Path::new(root).exists() {
        println!("open");
        Repository::open(root).unwrap()
    } else {
        println!("init");
        Repository::init(root).unwrap()
    };

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

    File::create(Path::new(root).join(file_name)).unwrap();

    index.add_path(Path::new(file_name)).unwrap();
    index.write().unwrap();
    let lc_result = find_last_commit(&repo);

    let mut parent_commits: Vec<&Commit> = vec![];

    match lc_result.as_ref() {
        Ok(c) => parent_commits.push(c),
        Err(_) => (),
    };

    let id = index.write_tree().unwrap();
    let tree = repo.find_tree(id).unwrap();

    repo.commit(
        Some("HEAD"),
        &repo.signature().unwrap(),
        &repo.signature().unwrap(),
        "commit message",
        &tree,
        &parent_commits,
    )
    .unwrap();
}

When i run it the first time

cargo run /path/to/testrepo file1.txt

It commits fine, but i need the write call

index.add_path(Path::new(file_name)).unwrap();
index.write().unwrap();
let lc_result = find_last_commit(&repo);

if i want call it again

cargo run /path/to/testrepo file2.txt

If i dont have the write call, it will just show file1.txt and file2.txt as deleted.

Do I need this write call in my example? Or am i using the library wrong? Or is this a bug? If i read the example init and the docs, it shouldn't be necessary to do the write, but my example wont work without.

I dont find it clear on how to use this correctly.

If i am using this correctly, it could be super awesome to have it stated more clearly in the docs or examples, that you need the write call.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by comparing the Index::write and Index::write_tree documentation with examples/init.rs and the provided two-run reproducer. Determine whether the example or API documentation needs clarification, then update the relevant documentation or example so the required index workflow and expected commit behavior are clear.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
documentation
Issue type
Documentation
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.