What is isomorphic-git?
isomorphic-git is a pure JavaScript implementation of Git that works in both Node.js and browser environments. It provides a wide range of Git functionalities, allowing developers to perform Git operations without relying on native Git installations.
What are isomorphic-git's main functionalities?
Clone a Repository
This feature allows you to clone a Git repository. The code sample demonstrates how to clone the isomorphic-git repository into a local directory named '/tutorial'.
const git = require('isomorphic-git');
const fs = require('fs');
(async () => {
await git.clone({
fs,
dir: '/tutorial',
url: 'https://github.com/isomorphic-git/isomorphic-git',
singleBranch: true,
depth: 1
});
console.log('Cloned the repository!');
})();
Commit Changes
This feature allows you to commit changes to a repository. The code sample demonstrates how to commit changes in the '/tutorial' directory with a commit message 'Initial commit'.
const git = require('isomorphic-git');
const fs = require('fs');
(async () => {
await git.commit({
fs,
dir: '/tutorial',
author: {
name: 'Your Name',
email: 'you@example.com'
},
message: 'Initial commit'
});
console.log('Committed changes!');
})();
Push to Remote
This feature allows you to push changes to a remote repository. The code sample demonstrates how to push changes from the '/tutorial' directory to the 'main' branch of the remote repository 'origin'.
const git = require('isomorphic-git');
const fs = require('fs');
(async () => {
await git.push({
fs,
dir: '/tutorial',
remote: 'origin',
ref: 'main',
token: 'your-github-token'
});
console.log('Pushed to remote!');
})();
Fetch from Remote
This feature allows you to fetch changes from a remote repository. The code sample demonstrates how to fetch changes from the 'origin' remote repository into the '/tutorial' directory.
const git = require('isomorphic-git');
const fs = require('fs');
(async () => {
await git.fetch({
fs,
dir: '/tutorial',
remote: 'origin'
});
console.log('Fetched from remote!');
})();
List Files in a Commit
This feature allows you to list files in a specific commit. The code sample demonstrates how to read the commit object for the 'HEAD' commit and log the tree of files.
const git = require('isomorphic-git');
const fs = require('fs');
(async () => {
const commit = await git.readCommit({
fs,
dir: '/tutorial',
oid: 'HEAD'
});
console.log(commit.commit.tree);
})();
Other packages similar to isomorphic-git
nodegit
NodeGit is a native Node.js binding to the libgit2 library, providing a comprehensive set of Git functionalities. Unlike isomorphic-git, NodeGit relies on native bindings and is not designed to work in browser environments.
simple-git
Simple-git is a lightweight wrapper around the Git command line interface, providing a simple API for common Git operations. It requires Git to be installed on the system and is not designed to work in browser environments, unlike isomorphic-git.
dugite
Dugite provides a simplified interface for using Git in Node.js applications. It bundles a specific version of Git with the package, ensuring consistent behavior across different environments. However, it does not support browser environments like isomorphic-git.
isomorphic-git
Node library for interacting with git repositories, circa 2017
(Originally I was going to call it esgit
but the name is too similar to another
project called es-git
.)
Progress
Porcelain:
Plumbing:
Note: There appears to be no a way to push signed commits back to Github using their API (v3 or v4), so I think we will have to use smart HTTP, packfiles, and an anti-CORS proxy.
High-level API (unstable)
This is analogous to the "porcelain" git commands. There is a single function git()
that serves as a fluent command builder.
Examples:
import git from 'esgit'
git('test').init()
git('.').githubToken(process.env.GITHUB_TOKEN).clone('https://github.com/wmhilton/esgit')
git('.').checkout('master')
git('.').list()
git('.').add('README.md')
git('.').remove('.env')
git('.')
.add('a.txt')
.author('Mr. Test')
.email('mrtest@example.com')
.signingKey('-----BEGIN PGP PRIVATE KEY BLOCK-----...')
.commit('Added the a.txt file')
git('.')
.setConfig('remote "origin".url', 'https://github-cors.now.sh/wmhilton/test.empty')
git('.')
.githubToken(process.env.GITHUB_TOKEN)
.remote('origin')
.push('refs/heads/master')
git().gitdir('my-bare-repo').workdir('/var/www/website')
CLI
I realized I could "translate" command line options into JavaScript chained commands
without hard-coding any knowledge of the API if I kept the chained commands very predictable.
I built a purely a generic translator and it worked surprisingly well.
So you can do any current or future esgit commands using the included esgit
CLI.
It always starts with an implicit git('.')
so it defaults to working in the
current working directory.
// Create a new empty repo
esgit --gitdir=test init
// Clone from a Github repository to the current working directory.
// Just like it's counterpart, clone is really just shorthand for git.init(); git.fetch(); git.checkout();
esgit --githubToken=$GITHUB_TOKEN clone https://github.com/wmhilton/esgit
// Checkout a commitish
esgit checkout master
// List files in the index
esgit list
// Add files to the index
esgit add README.md
// Remove files from the index
esgit remove .env
// Create a new commit (there's actually several more options for date, committer)
esgit add a.txt
esgit --author='Mr. Test' --email=mrtest@example.com --signingKey="$(cat private.key)" commit 'Added the a.txt file'
// And if you need to work with bare repos there are
// equivalents to the `--git-dir` and `--work-tree` options
esgit --gitdir=my-bare-repo --workdir=/var/www/website
Low-level API (also unstable)
The high-level makes some assumptions (like you have a file-system and network access) that might not be well suited
to your embedded git-based concept thingy. Fear not! I am
purposefully building this library as a series of small modules
so you can pick and choose features as you need them.
Individual commands
Each command is available as its own file, so hopefully with
a bit of finagling you will be able to do:
const add = require('esgit/lib/commands/add.js')
const checkout = require('esgit/lib/commands/checkout.js')
const config = require('esgit/lib/commands/config.js')
const fetch = require('esgit/lib/commands/fetch.js')
const init = require('esgit/lib/commands/init.js')
const list = require('esgit/lib/commands/list.js')
const remove = require('esgit/lib/commands/remove.js')
checkout({
workdir: '.',
gitdir: '.git',
ref: 'master',
remote: 'origin'
})
.then()
Individual objects
Only interested in commit objects? Import just the GitCommit class. And so on:
const GitBlob = require('esgit/lib/models/GitBlob.js')
const GitCommit = require('esgit/lib/models/GitCommit.js')
const GitTree = require('esgit/lib/models/GitTree.js')
const GitObject = require('esgit/lib/models/GitObject.js')
const commit = GitCommit.from(`tree 0370383003493b845c73fb4316355a18f0e346fe
parent c9efafe888804a58e333ec1b64ca58f9c4c92414
author Will Hilton <wmhilton@gmail.com> 1503898987 -0400
committer Will Hilton <wmhilton@gmail.com> 1503899426 -0400
gpgsig -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQIcBAABAgAGBQJZo68iAAoJEJYJuKWSi6a5WesP/31Ie7uNJcLU1+yEch1ubuhS
J9PfAna2iUflQANNq8BCyufW8Nq3ZA+nyRvaA499VqLkiuEI6nMUwWVPdhuYUG4s
P7QP85NXn1w1DRNfbb2K6USz40P3a108GqhOpDCLP55kRfcxyJZIymD/WtHKji7T
3ubE0meMYHOsx9a76Roo9DOHiqy3gAZsiwqvvqToD/IrmPmRv3qCqFBPrKKMW7la
DZGPFHV9bhJfaGTT42lu0vTjv5itdLwfCLReh3LuQ1TAq5b6hhvwguE+M+X8qA11
KXxxyQf06wt3+o8pjxxUHttVsuiS68yxzeTQOWe8pk/E5oaEqOx6O9stDvSfZatm
2LvFR++0axDMANluuStAoDhTMrqUqLQ3iWeay+xpivrP7mWnLM/bzrPShQVSkFPP
VkKedKiaaCGKTEY4xa/RBQvQ3WqDnNogY5t6bOolPvC5/D5n3grkzOpI7WQqh8EN
+6eYOOYTTPShi5gnBTSwFdSBKG4aWJI5moQtQJ1wQtc4Zhm4jKbp7OLPn1GJEET6
QwnURymv4mkDuvyPUki5nsP5U+xzWObp8PlqE4SCbSiV4D5FqF5uVmeh4E15WsTP
y2l3fT2JUVecDXOLot3MSceiakrgoKNsYQlM+e/+XHwuZ41nZ/OuSCBGjhRn0UDI
1qMqffk5gWiTsZFwLjBV
=gsxy
-----END PGP SIGNATURE-----
Add a .git/index parserializer`)
commit.parseHeaders()