es-git

repository·main·Indexed 43 days ago

https://github.com/toss/es-git

A high-performance Git library for Node.js providing a TypeScript-ready interface for complex Git operations. It leverages native bindings to libgit2 via napi-rs for speed and ease of installation, avoiding the need for node-gyp or local build tools.

Tokens
114.2K
Snippets
503
Records
631
Agent score
55%

What's inside es-git

  1. Overview of es-git

    main

    es-git is a modern Git library for Node.js designed with a simple and intuitive interface. It provides built-in TypeScript types for stable development and high performance by binding to libgit2 via git2-rs, built as a native module using napi-rs.

    Key features:

    • High Performance: Leverages native bindings for efficient Git operations.
    • Easy Installation: Provides pre-built native modules for various operating systems and CPU architectures, eliminating the need for node-gyp or local compilation.
    • TypeScript Support: Fully typed for a better developer experience.
  2. Why use es-git instead of child_process or nodegit

    main

    When integrating Git into a Node.js application, es-git offers several advantages over common alternatives:

    • vs child_process: es-git is significantly faster (up to 100x) because it avoids the overhead of spawning child processes and the complexity of parsing stdout. It provides a direct native interface to libgit2.
    • vs nodegit: es-git uses N-API for prebuilt native modules, whereas nodegit relies on node-gyp. This means es-git does not require a local build environment or specific system dependencies to be present during installation, making it more stable and easier to deploy across Windows, macOS, and Linux.
  3. Revert a commit with revert()

    main
    The revert method undoes the changes introduced by a specific commit. It achieves this by calculating the inverse of the changes in the target commit and applying those inverse changes to the current HEAD commit and the working directory. This effectively creates a new commit that reverses the effects of the specified commit without rewriting history.
  4. Push changes to a remote

    main

    Use the push(refspecs, options) method on a Remote instance to upload local changes to a remote repository.

    Refspecs should be provided in the format local_ref:remote_ref (e.g., refs/heads/main:refs/heads/other).

    If the remote requires authentication, provide a credential object in the options. For GitHub Personal Access Tokens, use the Plain type and provide the token as the password.

    import { openRepository } from 'es-git';
    
    const repo = await openRepository('/path/to/repo');
    const remote = repo.getRemote('origin');
    
    // Push the local "main" branch to the remote "other" branch
    await remote.push(['refs/heads/main:refs/heads/other']);
    
    // Push with authentication (e.g., GitHub Personal Access Token)
    await remote.push(['refs/heads/main:refs/heads/other'], {
      credential: {
        type: 'Plain',
        password: '<personal access token>',
      },
    });
  5. Install es-git

    main

    es-git is a modern Git library for Node.js that provides a simple and intuitive interface with built-in TypeScript support. It uses napi-rs to provide high-performance native bindings to libgit2 via git2-rs.

    Because it provides pre-built native modules tailored to your OS and CPU architecture, you can install it via npm without needing node-gyp or local build tools.

    npm install es-git
  6. Commit changes to a repository

    main

    To create a new commit in es-git, you must follow these steps:

    1. Stage changes: Use repo.index() to get the index and call addPath(path) or addAll(patterns) to stage files.
    2. Write the tree: Call index.writeTree() to create a tree object from the staged changes and retrieve its OID.
    3. Retrieve the tree object: Use repo.getTree(treeOid) to get the tree object required for the commit.
    4. Execute the commit: Call repo.commit(tree, message, options) providing the tree, commit message, and an options object containing updateRef, author, committer, and parents (usually the current HEAD target).
    import { openRepository } from 'es-git';
    import fs from 'node:fs/promises';
    
    const repo = await openRepository('.');
     
    await fs.writeFile('README.md', 'Hello World!', 'utf8');
    
    const index = repo.index();
    index.addPath('README.md');
    
    const treeOid = index.writeTree();
    const tree = repo.getTree(treeOid);
    
    const signature = { name: 'Seokju Na', email: 'seokju.me@toss.im' };
    const oid = repo.commit(tree, 'added new file', {
      updateRef: 'HEAD',
      author: signature,
      committer: signature,
      parents: [repo.head().target()!],
    });
    
    const commit = repo.getCommit(oid);
    console.log(commit.summary()); // "added new file"
  7. Authenticate cloning with HTTPS or Personal Access Tokens

    main

    For HTTPS repositories, use the Plain credential type. This is required for password-based authentication or when using a GitHub Personal Access Token to access private repositories.

    import { cloneRepository } from 'es-git';
    
    // Authenticate using a plain password
    const cloneWithPlain = await cloneRepository('https://github.com/toss/es-git', '.', {
      fetch: {
        credential: {
          type: 'Plain',
          password: 'MY_PASSWORD',
        },
      },
    });
    
    // Authenticate using a GitHub Personal Access Token
    const repo = await cloneRepository('https://github.com/<owner>/<repo>', '.', {
      fetch: {
        credential: {
          type: 'Plain',
          password: '<personal access token>',
        },
      },
    });
  8. Create signed GPG commits

    main

    You can create GPG signed commits by passing a signature string in the options object of the repo.commit() method. To retrieve the signature information from a commit, use repo.extractSignature(oid).

    import { openRepository } from 'es-git';
    
    const repo = await openRepository('.');
    const index = repo.index();
    const treeOid = index.writeTree();
    const tree = repo.getTree(treeOid);
    
    const signature = { name: 'Seokju Na', email: 'seokju.me@toss.im' };
    
    // Create a signed commit
    const oid = repo.commit(tree, 'signed commit', {
        updateRef: 'HEAD',
        author: signature,
        committer: signature,
        parents: [repo.head().target()!],
        signature: '-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v1\n\niQEcBAABAgAGBQJTest123\n-----END PGP SIGNATURE-----'
    });
    
    // Extract signature from the commit
    const signatureInfo = repo.extractSignature(oid);
    console.log(signatureInfo.signature);
  9. Explore es-git usage and API reference

    main

    You can explore how to use es-git through the following documentation sections:

    • Usage Guides: Learn how to interact with repositories via /usage/repository.
    • API Reference: Detailed technical documentation for specific classes and methods, such as openRepository, can be found at /reference/Repository/openRepository.
  10. Setup benchmarks environment

    main

    To prepare the environment for running benchmarks, you must install the dependencies for the nodegit package. Note that nodegit is managed separately in this workspace because it may fail to build locally if prebuilt binaries are unavailable.

    cd nodegit
    npm install
  11. Compare local HEAD with a remote branch

    main

    You can compare your local branch against a remote branch by first fetching the remote data. This is a common pattern for CI pipelines to determine changes in a Pull Request.

    1. Use remote.fetch() to update the local tracking branches.
    2. Use repo.getReference() to get the remote reference (e.g., refs/remotes/origin/main).
    3. Use peelToTree() on both references to get their trees.
    4. Use repo.diffTreeToTree() to compare them.
    import { openRepository } from 'es-git';
    
    const repo = await openRepository('.');
    const remote = repo.getRemote('origin');
    await remote.fetch(['main'], {
      fetch: {
        depth: 1,
      },
    });
    
    const baseTree = repo.getReference('refs/remotes/origin/main').peelToTree();
    const headTree = repo.head().peelToTree();
    
    // Diff between the tree pointed to by the remote "origin/main" branch and the tree pointed to by the local "HEAD".
    const diff = repo.diffTreeToTree(baseTree, headTree);
    
    for (const delta of diff.deltas()) {
      // ...
    }