git2-rs

repository·main·Indexed 24 days ago

https://github.com/rust-lang/git2-rs

Rust bindings for libgit2 (version 0.21.0) that allow developers to perform Git operations within Rust applications. The library provides functionality for managing Git configuration via the Config struct, handling the Git index and staging area through the Index and IndexEntry structs, and supporting network operations via optional https and ssh features.

Tokens
12K
Snippets
13
Records
76
Agent score
84%

What's inside git2

  1. Configure libgit2 linking behavior

    main

    The library requires libgit2 1.9.6 or newer. The libgit2-sys crate handles building the library for you, but you can control how it is linked.

    By default, the vendored libgit2 is linked statically if:

    1. The environment variable LIBGIT2_NO_VENDOR=1 is not set.
    2. AND either the vendored-libgit2 Cargo feature is enabled, or an appropriate version of libgit2 cannot be found on the system.

    Note that the LIBGIT2_NO_VENDOR=1 environment variable overrides the Cargo feature.

  2. Enable network support (HTTPS and SSH) in git2

    main

    If you need to clone remote repositories or support user-provided URLs, you must enable the https and/or ssh features. To enable both, use the following command:

    cargo add git2 --features https,ssh
  3. Manage the Git index with the `Index` struct

    main

    The Index struct represents a Git index (staging area). You can create an in-memory index using Index::new() or Index::new_ext(format), or open an existing index from disk using Index::open(path) or Index::open_ext(path, format).

    Key Operations:

    • Adding entries: Use add(&IndexEntry) for in-memory entries, add_frombuffer(&IndexEntry, &[u8]) to create a blob from memory, or add_path(&Path) to add a file from the working directory.
    • Batch operations: add_all(pathspecs, flag, callback) and remove_all(pathspecs, callback) allow for pattern-based updates using pathspecs.
    • Persistence: Changes made to an in-memory Index are not saved to disk until write() is called.
    • Tree creation: write_tree() scans the index and returns the OID of the resulting root tree, which can be used to create commits.
  4. Traverse commit history with Revwalk

    main

    A Revwalk allows you to traverse the commit graph by specifying one or more 'leaves' (commits to start from) and excluding one or more 'roots' (commits to hide).

    To use a Revwalk, you typically obtain it from a Repository instance, push the starting points (leaves), and then iterate over the resulting Oids.

    Key lifecycle notes:

    • The Revwalk is automatically reset when iteration completes.
    • You can manually call reset() to re-configure the walker.
    • It implements Iterator, yielding Result<Oid, Error>.
    let mut walk = repo.revwalk().unwrap();
    walk.push(target).unwrap();
    
    // Collect all OIDs in the walk
    let oids: Vec<crate::Oid> = walk.by_ref().collect::<Result<Vec<_>, _>>().unwrap();
  5. How TreeWalkMode and TreeWalkResult work

    main

    When using tree.walk(), the traversal behavior is governed by the TreeWalkMode and the value returned by your callback function (TreeWalkResult).

    1. PreOrder: The callback is called on a directory (tree) first, then on its contents. If you return TreeWalkResult::Skip on a directory, the walker will not enter that directory.
    2. PostOrder: The callback is called on the contents of a directory first, and then on the directory itself.
    3. Control Flow: Returning TreeWalkResult::Abort immediately terminates the entire walking process, which is useful for finding a specific item and stopping early.
    tree.walk(TreeWalkMode::PreOrder, |_, entry| {
        if entry.name().unwrap() == "target_file.txt" {
            return TreeWalkResult::Abort;
        }
        TreeWalkResult::Ok
    }).unwrap();
  6. Use `IndexMatchedPath` callback for filtering

    main

    Methods like add_all, remove_all, and update_all accept an optional callback of type IndexMatchedPath. This callback is invoked for each item that matches the provided pathspecs.

    Callback Signature: FnMut(&Path, &[u8]) -> i32

    Return Values:

    • 0: Confirm the operation (add/remove/update) on the item.
    • > 0: Skip the item.
    • < 0: Abort the entire scan and return an error.
  7. Handle merge conflicts in the index

    main

    When a merge conflict occurs, the index contains multiple entries for the same path at different stages.

    Detecting Conflicts:

    • Use has_conflicts() to check if the index contains any conflicts.
    • Use conflicts() to get an iterator over all conflicting entries.

    Inspecting a Specific Conflict: Use conflict_get(path) to retrieve an IndexConflict object for a specific file. This object provides access to:

    • ancestor: The common ancestor entry (if any).
    • our: The entry from the local/user repository.
    • their: The entry from the external/remote repository.

    Resolving Conflicts: Use conflict_remove(path) to remove the conflicting entries for a specific path.

  8. Implement a smart subtransport stream

    main

    A SmartSubtransportStream is the object used to actually read and write data during the Git protocol negotiation.

    Because SmartSubtransportStream is automatically implemented for any type that implements std::io::Read + std::io::Write + Send + 'static, you can simply use any existing type that meets these bounds (like a TCP stream or a custom buffer) without manual trait implementation.

  9. Implement a smart subtransport

    main

    To implement a 'smart' transport (one that uses the Git smart protocol to negotiate data), you must implement the SmartSubtransport trait. This trait handles the high-level logic of connecting to a URL and performing specific Git services.

    Key requirements:

    • action(&self, url: &str, action: Service): Responsible for establishing the network connection and returning a stream (Box<dyn SmartSubtransportStream>) for the requested Service.
    • close(&self): Terminates the connection with the remote.

    Supported Service actions:

    • UploadPackLs
    • UploadPack
    • ReceivePackLs
    • ReceivePack
  10. Set the scope of status reporting with StatusShow

    main

    The StatusShow enum determines which comparisons are performed during a status operation:

    • StatusShow::Index: Only compares HEAD to the index (ignores working directory changes).
    • StatusShow::Workdir: Only compares the index to the working directory (ignores HEAD).
    • StatusShow::IndexAndWorkdir: The default behavior, comparing both index and working directory (similar to git status --porcelain).
  11. Iterate over configuration entries

    main

    You can iterate over all configuration variables or specific subsets using entries() and multivar().

    Iterating all entries

    Use entries(glob: Option<&str>). If a glob is provided, it filters variables whose names match the pattern (case-sensitively on the normalized name).

    Iterating multivar entries

    Use multivar(name: &str, regexp: Option<&str>). This iterates over the values of a specific multivar name. If a regexp is provided, it filters values that match the pattern.

    Using ConfigEntries

    Because of lifetime restrictions, ConfigEntries does not implement the standard Iterator trait. Instead, use:

    • next(): Advances the iterator to the next ConfigEntry.
    • for_each(closure): A convenience method to apply a function to every entry.

    Note: ConfigEntry provides access to the entry's name(), value(), level(), and include_depth().