A JavaScript implementation of Git.

Related tags

Mad science js-git
Overview

JS-Git

Gitter

This project is a collection of modules that helps in implementing git powered applications in JavaScript. The original purpose for this is to enable better developer tools for authoring code in restricted environments like ChromeBooks and tablets. It also enables using git as a database to replace SQL and no-SQL data stores in many applications.

This project was initially funded by two crowd-sourced fundraisers. See details in BACKERS.md and BACKERS-2.md. Thanks to all of you who made this possible!

Usage

Detailed API docs are contained in the doc subfolder of this repository.

In general the way you use js-git is you create a JS object and then mixin the functionality you need. Here is an example of creating an in-memory database, creating some objects, and then walking that tree using the high-level walker APIs.

Creating a repo object.

// This provides symbolic names for the octal modes used by git trees.
var modes = require('js-git/lib/modes');

// Create a repo by creating a plain object.
var repo = {};

// This provides an in-memory storage backend that provides the following APIs:
// - saveAs(type, value) => hash
// - loadAs(type, hash) => hash
// - saveRaw(hash, binary) =>
// - loadRaw(hash) => binary
require('js-git/mixins/mem-db')(repo);

// This adds a high-level API for creating multiple git objects by path.
// - createTree(entries) => hash
require('js-git/mixins/create-tree')(repo);

// This provides extra methods for dealing with packfile streams.
// It depends on
// - unpack(packStream, opts) => hashes
// - pack(hashes, opts) => packStream
require('js-git/mixins/pack-ops')(repo);

// This adds in walker algorithms for quickly walking history or a tree.
// - logWalk(ref|hash) => stream<commit>
// - treeWalk(hash) => stream<object>
require('js-git/mixins/walkers')(repo);

// This combines parallel requests for the same resource for efficiency under load.
require('js-git/mixins/read-combiner')(repo);

// This makes the object interface less strict.  See its docs for details
require('js-git/mixins/formats')(repo);

Generators vs Callbacks

There are two control-flow styles that you can use to consume js-git APIs. All the examples here use yield style and assume the code is contained within a generator function that's yielding to a tool like gen-run.

This style requires ES6 generators. This feature is currently in stable Firefox, in stable Chrome behind a user-configurable flag, in node.js 0.11.x or greater with a command-line flag.

Also you can use generators on any ES5 platform if you use a source transform like Facebook's regenerator tool.

You read more about how generators work at Generators vs Fibers.

var run = require('gen-run');

run(function*() {
 // Blocking logic goes here.  You can use yield
 var result = yield someAction(withArgs);
 // The generator pauses at yield and resumes when the data is available.
 // The rest of your process is not blocked, just this generator body.
 // If there was an error, it will throw into this generator.
});

If you can't use this new feature or just plain prefer node-style callbacks, all js-git APIs also support that. The way this works is actually quite simple. If you don't pass in the callback, the function will return a partially applied version of your call expecting just the callback.

someAction(withArgs, function (err, value) {
  if (err) return handleMyError(err);
  // do something with value
});

// The function would be implemented to support both style like this.
function someAction(arg, callback) {
  if (!callback) return someAction.bind(this, arg);
  // We now have callback and arg
}

Basic Object Creation

Now we have an in-memory git repo useful for testing the network operations or just getting to know the available APIs.

In this example, we'll create a blob, create a tree containing that blob, create a commit containing that tree. This shows how to create git objects manually.

  // First we create a blob from a string.  The `formats` mixin allows us to
  // use a string directly instead of having to pass in a binary buffer.
  var blobHash = yield repo.saveAs("blob", "Hello World\n");

  // Now we create a tree that is a folder containing the blob as `greeting.txt`
  var treeHash = yield repo.saveAs("tree", {
    "greeting.txt": { mode: modes.file, hash: blobHash }
  });

  // With that tree, we can create a commit.
  // Again the `formats` mixin allows us to omit details like committer, date,
  // and parents.  It assumes sane defaults for these.
  var commitHash = yield repo.saveAs("commit", {
    author: {
      name: "Tim Caswell",
      email: "[email protected]"
    },
    tree: treeHash,
    message: "Test commit\n"
  });

Basic Object Loading

We can read objects back one at a time using loadAs.

// Reading the file "greeting.txt" from a commit.

// We first read the commit.
var commit = yield repo.loadAs("commit", commitHash);
// We then read the tree using `commit.tree`.
var tree = yield repo.loadAs("tree", commit.tree);
// We then read the file using the entry hash in the tree.
var file = yield repo.loadAs("blob", tree["greeting.txt"].hash);
// file is now a binary buffer.

When using the formats mixin there are two new types for loadAs, they are "text" and "array".

// When you're sure the file contains unicode text, you can load it as text directly.
var fileAsText = yield repo.loadAs("text", blobHash);

// Also if you prefer array format, you can load a directory as an array.
var entries = yield repo.loadAs("array", treeHash);
entries.forEach(function (entry) {
  // entry contains {name, mode, hash}
});

Using Walkers

Now that we have a repo with some minimal data in it, we can query it. Since we included the walkers mixin, we can walk the history as a linear stream or walk the file tree as a depth-first linear stream.

// Create a log stream starting at the commit we just made.
// You could also use symbolic refs like `refs/heads/master` for repos that
// support them.
var logStream = yield repo.logWalk(commitHash);

// Looping through the stream is easy by repeatedly calling waiting on `read`.
var commit, object;
while (commit = yield logStream.read(), commit !== undefined) {

  console.log(commit);

  // We can also loop through all the files of each commit version.
  var treeStream = yield repo.treeWalk(commit.tree);
  while (object = yield treeStream.read(), object !== undefined) {
    console.log(object);
  }

}

Filesystem Style Interface

If you feel that creating a blob, then creating a tree, then creating the parent tree, etc is a lot of work to save just one file, I agree. While writing the tedit app, I discovered a nice high-level abstraction that you can mixin to make this much easier. This is the create-tree mixin referenced in the above config.

// We wish to create a tree that contains `www/index.html` and `README.me` files.
// This will create these two blobs, create a tree for `www` and then create a
// tree for the root containing `README.md` and the newly created `www` tree.
var treeHash = yield repo.createTree({
  "www/index.html": {
    mode: modes.file,
    content: "<h1>Hello</h1>\n<p>This is an HTML page?</p>\n"
  },
  "README.md": {
    mode: modes.file,
    content: "# Sample repo\n\nThis is a sample\n"
  }
});

This is great for creating several files at once, but it can also be used to edit existing trees by adding new files, changing existing files, or deleting existing entries.

var changes = [
  {
    path: "www/index.html" // Leaving out mode means to delete the entry.
  },
  {
    path: "www/app.js", // Create a new file in the existing directory.
    mode: modes.file,
    content: "// this is a js file\n"
  }
];

// We need to use array form and specify the base tree hash as `base`.
changes.base = treeHash;

treeHash = yield repo.createTree(changes);

Creating Composite Filesystems

The real fun begins when you create composite filesystems using git submodules.

The code that handles this is not packaged as a repo mixin since it spans several independent repos. Instead look to the git-tree repo for the code. It's interface is still slightly unstable and undocumented but is used in production by tedit and my node hosting service that complements tedit.

Basically this module allows you to perform high-level filesystem style commands on a virtual filesystem that consists of many js-git repos. Until there are proper docs, you can see how tedit uses it at https://github.com/creationix/tedit-app/blob/master/src/data/fs.js#L11-L21.

Mounting Github Repos

I've been asking Github to enable CORS headers to their HTTPS git servers, but they've refused to do it. This means that a browser can never clone from github because the browser will disallow XHR requests to the domain.

They do, however, offer a REST interface to the raw git data.

Using this I wrote a mixin for js-git that uses github as the backend store.

Code at https://github.com/creationix/js-github. Usage in tedit can be seen at https://github.com/creationix/tedit-app/blob/master/src/data/fs.js#L31.

Comments
  • Simple streams feedback

    Simple streams feedback

    See full spec here

    Sinks as object

    I think having { consume } or some other name would be nice

    var stream = { read, stop }
    var sink = { consume }
    

    cc @dominictarr

    opened by Raynos 64
  • check out chrisdickinson's recent work

    check out chrisdickinson's recent work

    apologies if you guys have already discussed this. @chrisdickinson has been working on a js git implementation lately:

    https://github.com/chrisdickinson/git-fetch-pack https://github.com/chrisdickinson/git-walk-tree https://github.com/chrisdickinson/git-odb https://github.com/chrisdickinson/git-odb-pack https://github.com/chrisdickinson/git-packfile https://github.com/chrisdickinson/git-walk-refs https://github.com/chrisdickinson/git-parse-human https://github.com/chrisdickinson/git-transport-protocol https://github.com/chrisdickinson/git-read-pkt-line https://github.com/chrisdickinson/git-write-pkt-line https://github.com/chrisdickinson/git-load-refs https://github.com/chrisdickinson/git-packidx-parser https://github.com/chrisdickinson/git-packed-ref-parse https://github.com/chrisdickinson/git-to-js https://github.com/chrisdickinson/git-object-tree https://github.com/chrisdickinson/git-object-tag https://github.com/chrisdickinson/git-object-commit https://github.com/chrisdickinson/git-object-blob https://github.com/chrisdickinson/git-odb-loose https://github.com/chrisdickinson/git-autoregister-odb https://github.com/chrisdickinson/git-apply-delta

    etc

    opened by maxogden 16
  • Read / write to private Bitbucket repo

    Read / write to private Bitbucket repo

    Thanks for this awesome project.

    Looking to read / write files to private bitbucket.org repositories using js-git. Is this possible?

    If not, is it on the roadmap?

    Guessing I would need to supply js-git with a private key somehow.

    opened by peterjenkins 10
  • Design fs.*, db.*, env.* and git.* public APIs

    Design fs.*, db.*, env.* and git.* public APIs

    The first minimal UI for js-git will be js repl based. There are three object interfaces that need to be implemented.

    db

    This is a dumb key/value interface

    • db.get(key, callback) - Get a value from the database by key.
    • db.set(key, value, callback) - Write a value to the database.
    • what else do we need? Maybe delete?

    db conventions for git

    We'll have some conventions for mapping a git database to a generic k/v store.

    • objects are keyed by their sha1 hash (of the value itself)
    • a repository is keyed by it's random uuid (for example f47ac10b-58cc-4372-a567-0e02b2c3d479)

    In the fs abstraction, .gitid files will contain the uuid of the git repo they reference.

    fs

    This is a generic filesystem abstraction. It represents a filesystem that may contain several git repos (though the actual git databases are stored in the db instance)

    • fs.readfile(path, callback) - the data will be null if the file doesn't exist instead of emitting an error.
    • fs.writefile(path, contents, callback) - write contents to a file, creating it if it doesn't exist.
    • fs.rmfile(path, callback) - delete a file
    • fs.readdir(path, callback) - get a listing of files in a path as an array of strings.
    • fs.mkdir(path, callback) - create a folder, creating parent directories if needed.
    • fs.rmdir(path, callback) - delete a folder and all it's contents

    env

    An environment is like a bash instance. It has local state. For now this means current working directory. It also can integrate with the rest of the app and do things like open files in gui programs (text editors, image viewers, html engines, etc..)

    • env.chdir(path) - change the working directory. If this env is attached to a git instance, it notifies git of the change.
    • env.cwd() - Return the current working directory
    • env.open(path, callback) - open a file for viewing in the default app (preview html)
    • env.edit(path, callback) - open a file for editing in the default app (edit html)

    git env

    This represents git porcelain coupled with an environment. It lets you do manage your git repos. The current working directory and current working git repo are implicit state.

    • git.init(callback) - create a new git repo in the current working directory
    • git.remotes - an object containing remote names and full urls. (TODO: how can we detect changes to this object, maybe a nextTick handler on a getter for .remotes?)
    • git.hashObject(object) - store an git object (string) in the git database by it's sha1 hash value.
    • git.updateIndex(...)
    • ... and other stuff as we implement them
    opened by creationix 10
  • Implement db interface on top of IndexedDb

    Implement db interface on top of IndexedDb

    Now that the interface is documented and there are three existing db implementations (filesystem, memory, localStorage), this should be to the point that someone else can take it on.

    This would be very useful since most platforms have indexedDB and it should perform better than the other options.

    Task 
    opened by creationix 9
  • Pull / push

    Pull / push

    We've discussed a little bit about the challenges of interfacing with remote repositories (https://github.com/creationix/js-git/issues/122 and other issues).

    I've noticed that this repository implemented clone using js-git utilities so it could be good to study.

    Putting the protocol issues and conflicts / merging aside for a moment, I was wondering how one might go about implementing push and pull? for example, is there is sample code (even partially complete)? if not, what js-git utils would one use and how? Are there js-git utils that are missing and would need to be written?

    Just trying to scope the problem and hoping for some pointers to see how to move this forward.

    opened by kmalakoff 7
  • bountysource.com/fundraisers is dead and the about link is sad

    bountysource.com/fundraisers is dead and the about link is sad

    https://www.bountysource.com/fundraisers/325-js-git as well as any of the /fundraisers functionality is abandoned and no more hosted there. Would you link to an archived page, or some description file?

    Documentation 
    opened by Huge 6
  • Data integrity fix, only expose final file

    Data integrity fix, only expose final file

    fs.writeFile will truncate the file, then write, during which time other readers will get a zero length file or a partial (corrupted) file. Fix by writing to a temporary file as the original git tools do, add .lock to a refs file, and tmp_obj_ for object files.

    I was reading a branch ref and kept getting an empty hash, then later would get the expected hash. Fix by writing into a temporary file then doing a rename after. At least on Unix that's an atomic operation, any reader will either get the old value or the new value, but never the 0 length or truncated value as it is being written out. Especially for branches, if the program crashed or was killed at the wrong moment a whole tree could be lost if the refs/heads/master was in the process of being written, but was left empty.

    As above this change avoids being told the repository is corrupt, the file is empty, that's why it is being flagged as an error. git-fsck Checking object directories: 100% (256/256), done. error: object file ./objects/ad/3fb1076f9d689b566ea5200e2a4565f52e796a is empty fatal: loose object ad3fb1076f9d689b566ea5200e2a4565f52e796a (stored in ./objects/ad/3fb1076f9d689b566ea5200e2a4565f52e796a) is corrupt

    opened by dfries 6
  • Pre existing repo

    Pre existing repo

    If I have a pre existing repo, cloned from some source, and now it's existed as file system with .git sub-directory. Can I deal with it using js-git and load it as repo object ?

    opened by rashad612 6
  • Document platform requirements

    Document platform requirements

    Would be good to document what platform implementations need to provide, eg. For bare repos need:

    • sha1
    • inflate
    • deflate
    • object db

    then additionally for working dir support also need:

    • fs
    • index db

    So for the repo browser app for example, only the bare repo set is needed from the platform.

    Documentation 
    opened by maks 6
  • Add proper unit tests for push-to-pull

    Add proper unit tests for push-to-pull

    This is for @neilk

    The newly created https://github.com/creationix/push-to-pull package needs some good unit tests.

    An example of what I want can be seen in https://github.com/creationix/min-stream/tree/master/test

    Use tape and test in browsers and in node. I'll hook it up to travis and testling got CI. You can use @chrisdiskinson's beefy server for the testling tests I think.

    push-to-pull needs to test the various combinations:

    • Sync source, async source
    • fast sink, slow sink
    • push-filters that output more than they input, push-filters that output less than they input
    opened by creationix 6
  • support simple HTTP protocol

    support simple HTTP protocol

    I'd like to use js-git in my project: https://ganarchy.autistic.space/project/385e734a52e13949a7a5c71827f6de920dbfea43/

    to provide a web UI for repos, when there's no web UI available. unfortunately most of my repos use the simple HTTP protocol, which doesn't even need CORS or anything, but js-git doesn't support it. those repos are the main (and, arguably the only) use-case I have for this. can we get support for that?

    opened by SoniEx2 25
  • Typo in mem-cache dupe?

    Typo in mem-cache dupe?

    This line checks the length of type which is always "blob" in this context. Likely it meant to check the length of value?

    https://github.com/creationix/js-git/blob/81321539d3a3d82c5046d683d8f77853fb63453a/mixins/mem-cache.js#L41

    opened by lionello 0
  • resolveRef should be available through a mix-in

    resolveRef should be available through a mix-in

    https://github.com/creationix/js-git/blob/bc266d7857cf098c570c5007475eb0fe2a68d43f/mixins/walkers.js#L100

    To do something, I have to basically copy this entire function into my own code. It would be useful if this were available through a mix-in.

    opened by binki 2
  • Fix commit ordering and traversal for logWalk

    Fix commit ordering and traversal for logWalk

    The compare function was comparing objects instead of seconds, always returning the same result (false, I believe). I believe it is correct to compare just seconds without the offset, as I believe seconds should be since the Unix epoch which is timezone independent, but I'm not positive. When fixed, it revealed that the seen map was not being updated during the walk, leading to duplicate commits being emitted (would walk down both sides of a merge and then the shared history twice).

    opened by GTRI-nathan 1
  • Clone URL in browser?

    Clone URL in browser?

    Sorry if there's something I'm missing here...

    I'd like to clone git repos in the browser by URL. I understand there are CORS issues, but I can set up a proxy.

    I've got GitHub working fine (using js-github) but can't figure out how to work with non-GitHub URLs. I found git-web-platform, but it doesn't seem to be published.

    Any pointers?

    Thanks! -Bobby

    opened by rbren 9
Owner
Tim Caswell
Making the world better and more free, one technology at a time.
Tim Caswell
IPFS implementation in JavaScript

The JavaScript implementation of the IPFS protocol Upgrading from <=0.40 to 0.48? See the release notes for the list of API changes and the migration

IPFS 7.2k Jan 8, 2023
🤖 GPU accelerated Neural networks in JavaScript for Browsers and Node.js

brain.js GPU accelerated Neural networks in JavaScript for Browsers and Node.js About brain.js is a GPU accelerated library for Neural Networks writte

brain.js 13.4k Jan 7, 2023
A JavaScript PDF generation library for Node and the browser

PDFKit A JavaScript PDF generation library for Node and the browser. Description PDFKit is a PDF document generation library for Node and the browser

null 8.5k Jan 2, 2023
A modular geospatial engine written in JavaScript

A modular geospatial engine written in JavaScript turfjs.org Turf is a JavaScript library for spatial analysis. It includes traditional spatial operat

turf 7.6k Jan 3, 2023
A javascript Bitcoin library for node.js and browsers.

BitcoinJS (bitcoinjs-lib) A javascript Bitcoin library for node.js and browsers. Written in TypeScript, but committing the JS files to verify. Release

bitcoinjs 4.8k Jan 1, 2023
A pure JavaScript implementation of git for node and browsers!

isomorphic-git isomorphic-git is a pure JavaScript reimplementation of git that works in both Node.js and browser JavaScript environments. It can read

isomorphic-git 6.7k Jan 4, 2023
A JavaScript implementation of Git.

JS-Git This project is a collection of modules that helps in implementing git powered applications in JavaScript. The original purpose for this is to

Tim Caswell 3.8k Dec 31, 2022
This is a template project demonstrating how the MERN stack(Mongo, Express, React, Node) can be used, here we have the back end implementation and there is the React implementation as the front end

Versão em português MERN stack This is a template project demonstrating how the MERN stack(Mongo, Express, React, Node) can be used, here we have the

Douglas Samuel Gonçalves 2 Jan 22, 2022
Modern native Git hooks made easy 🐶 woof!

husky Modern native Git hooks made easy Husky improves your commits and more ?? woof! Install npm install husky -D Usage Edit package.json > prepare

null 28.4k Jan 4, 2023
Git commit CLI

commitme Based on this gist by @gustavopch Installation You can install this on your package using npm i -D commitme or run it once using npx commitme

Roz 7 Jun 6, 2021
React.js todo app with authorization, Testing: Cypress(e2e), UI (Chakra UI), Eslint, prettier, git hooks

React TypeScript template with Vite This is a React + TypeScript boilerplate built with Vite. What's inside? ReactJS Vite TypeScript Jest Testing Libr

Vladislav 2 Jan 28, 2022
Repository for hands on practice in Git and GitHub workshop

Git and Github Workshop Jan 2022 Successful contributors ✨ of this project will be featured on the GDSC website so as to bring attraction and learn vi

IIIT Vadodara Open Source 2 Feb 10, 2022
here in this git repo you will get react js basic layout, having in responsive mode.

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 3 Feb 23, 2022
Visualize the Directed Acyclic Graph that Git creates to connect Commit, Tree and Blob objects internally.

Git Graph Visualize the Directed Acyclic Graph that Git creates to connect Commit, Tree and Blob objects. Hosted at HarshKapadia2.github.io/git-graph.

Harsh Kapadia 15 Aug 21, 2022
Get a diff view of your Obsidian Sync, File Recovery and Git version history

Version History Diff (for Sync and File Recovery Core plugins and Git) Note This plugin uses private APIs, so it may break at any time. Use at your ow

null 39 Dec 26, 2022
A small CLI tool to create a semantic release and git-cliff powered Changelog

cliff-jumper A small CLI tool to create a semantic release and git-cliff powered Changelog Description When managing a collection of projects you ofte

Favware 15 Sep 22, 2022
⚡ Automatically syncs Holy Unblocker to Github from git.holy.how

Holy Unblocker - Cloned to Github from git.holy.how What is the purpose of this repository? Holy Unblocker is self-hosted on git.holy.how. Personally,

IsaacLK 4 Nov 22, 2022
Glob-aware two-way copying for git

ggcp git-glob-copy — glob-aware two-way copying for git Requirements Node.js >= 16 Install npm i git-glob-cp # or as a global package npm i -g ggcp U

Anton Golub 3 Jul 5, 2022
🧘‍♀️ Chamomile simplifies issue and git tracking from the command line.

Chamomile What is Chamomile? Chamomile simplifies issue and git tracking from the command line. As of now the focus is bridging workflows involving Gr

Zev Stravitz 3 Aug 4, 2022
Markdown note maker (with Git sync) using Tauri.

Mediocre Markdown note maker (with Git sync) using Tauri. Screens Tech Stack Frontend Monaco Editor for the editor interface Chakra UI library Redux T

Nilay Savant 14 Dec 6, 2022