npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

json-git

v0.2.0

Published

A pure JS local Git to versionize any JSON

Downloads

5

Readme

json-git Build Status

A pure JS local Git to versionize any JSON.

If you want to use it with Redux find the official bindings here: json-git-redux.

Why should I use this?

The purpose of json-git is not to replace Git.

It is an experiment about bringing Git to the frontend for any Javascript application (like a react application).

Installation

It is avaible through npm:

npm install json-git

Usage

Create a repository

json-git exports only one method createRepository:

import createRepository from 'json-git';

const repository = createRepository();

It's that simple!

Your first commit

You have now a JSON git, it's time to do your first commit:

const tree = {
    foo: 'bar',
};

repository.commit('robin', 'first commit', tree);

robin is my author name for that commit, and of course first commit is the commit's message.

Read the current tree

At anytime you can read your current tree by getting the tree property :

console.log(repository.tree);

This will display:

{
    "foo": "bar",
}

Create your first branch

In json-git you always have a master branch but you can create some others as many as you want:

repository.checkout('dev', true); // true means the branch is new

You can now commit on that branch:

const tree = {
    foo: 'bar',
    hello: 'you',
    bar: true,
};

repository.commit('robin', 'second commit', tree);

If you display the tree you will get:

{
    "foo": "bar",
    "hello": "you",
    "bar": true,
}

Change the current branch

Well, we would like to come back to master:

repository.checkout('master');

This time, if you display the tree you will get:

{
    "foo": "bar",
}

Our "hello": "world" is missing because it was commited on dev branch not master.

Tip: Use the branch property of your repository to know the current branch.

Merge a branch into the current branch

No conflicts

The better situation for a merge is when there aren't any conflicts. So if we merge our dev branch into master it should be ok:

repository.merge('robin', 'dev');

This will automaticaly create a merge commit with robin as author.

With conflicts

Forget about our last merge, let's talk about conflicts. First let's commit a new tree on master:

const tree = {
    foo: 'lorem',
    hello: 'me',
};

repository.commit('robin', 'an evil commit', tree);

We've changed the foo value and added an hello key too! Let's try to merge dev into master:

repository.merge('robin', 'dev');

Well... it still works. Don't worry it is a normal behaviour. As we are dealing with simple JSON and not a full tree of files and folders, by default json-git will always give priority to the new version .

To review conflicts you must provide a resolver to the merge method:

function resolver(targetPatch /* dev branch */, localPatch /* master branch */, reject) {
    // this resolver will be called on each conflict
    // you receive the two patches which overlap
    // if you do nothing, the targetPatch wins
    // if you call reject, the localPatch wins
}

repository.merge('robin', 'dev', resolver);

So if we don't call reject(), the tree will look like this:

{
    "foo": "lorem",
    "hello": "you",
    "bar": true,
}

But if we call reject() to reject the hello conflict, the tree will look like this:

{
    "foo": "lorem",
    "hello": "me",
    "bar": true,
}

Tip: a merge won't delete the branch you've just merged. You must use repository.deleteBranch() for that.

Revert a commit

You can revert at anytime the changes introduced by a commit. It will generate a patch representing the diff between this commit and its parent, and then it will apply and commit it:

repository.revert('robin', '9acf3199dc573910e7f8ed6aaf9ae3d50a174bc9');

This will automaticaly create a revert commit with robin as author.

As for the merge, the conflict policy is the same (always works until you provide a resolver):

function resolver(patch, localValue, reject) {
    // this resolver will be called on each conflict
    // you receive the patch that json-git want to apply and the current value of the target node
    // if you do nothing, the patch wins
    // if you call reject, the current value wins

    // a conflict on revert can happen for example when the revert want to remove an already removed node.
}

repository.revert('robin', '9acf3199dc573910e7f8ed6aaf9ae3d50a174bc9', resolver);

Getting the full log of the repository

As you've seen, we've been using the commit hash for our revert() method. There are two ways to retrieve a commit hash :

  • The commit() method returns the hash of the new commit
  • The log property return the full history of your repository indexed by commit hashes

For example if we revert the merge commit, the log could look like this:

{
    "2de9314077d9074bb0ca57cb94e7f455b198db5e": {
        "author": "robin",
        "date": "2017-02-07T11:58:15.592Z",
        "message": "first commit",
        "treeHash": "a5e744d0164540d33b1d7ea616c28f2fa97e754a",
        "parent": "0000000000000000000000000000000000000000",
    },
    "ddfa215a540b0a43e6ae67b0b3893e355b8c06f7":
    {
        "author": "robin",
        "date": "2017-02-07T11:58:15.623Z",
        "message": "second commit",
        "treeHash": "3420a96c38d2a469cf4b029a8a39edd927976d86",
        "parent": "2de9314077d9074bb0ca57cb94e7f455b198db5e"
    },
    "f46b2822b2c7e3f88139789f7c14c83d8a85843a": {
        "author": "robin",
        "date": "2017-02-07T11:58:15.625Z",
        "message": "an evil commit",
        "treeHash": "e81607575aa673052e6cba65d14fee88ae7504ca",
        "parent": "2de9314077d9074bb0ca57cb94e7f455b198db5e"
    },
    "9acf3199dc573910e7f8ed6aaf9ae3d50a174bc9": {
        "author": "robin",
        "date": "2017-02-07T11:58:15.628Z",
        "message": "Merge of dev into master",
        "treeHash": "932b670bb8ccdc53606e51ef5d71ea85748b9d86",
        "parent": "f46b2822b2c7e3f88139789f7c14c83d8a85843a"
    },
    "9f866f45395ce63425113f50165d3b9371e04a8f": {
        "author": "robin",
        "date": "2017-02-07T11:58:15.630Z",
        "message": "Revert of commit 9acf3199dc573910e7f8ed6aaf9ae3d50a174bc9",
        "treeHash": "1c1713b5f507d9b70e50dcff649d5aa4574b6da7",
        "parent": "9acf3199dc573910e7f8ed6aaf9ae3d50a174bc9"
    }
}

Generate a diff

You can generate a diff between two branches or commits by using the diff() command:

repository.diff('master', 'dev'); // understand: Give me the patch I need to apply to master if I want to get dev state

The output is a JSON Patch, and looks like this:

[
    { "op": "add", "path": "/bar", "value": true },
    { "op": "replace", "path": "/foo", "value": "bar" },
    { "op": "replace", "path": "/hello", "value": "you" }
]

Apply a patch

You can apply a JSON Patch to the current tree by using the apply() method:

repository.apply(patch);

Tip: It will return the result but won't make a new commit. If you want to keep the result, it's up to you to commit it.

If you want to deal with the conflicts that may have occured, it works the same way as the revert() method above.

Delete a branch

When you're done with a branch, you can delete it. It is impossible to delete master branch for obvious reasons. The deletion only removes the head pointer, it doesn't remove the commits from the history. We should have a garbage collector for that (in future updates):

repository.deleteBranch('dev');

List of branches

To retrieve the list of branches, use the branches property: console.log(repository.branches).

Save and load your repository

Of course you can always export your repository with its toJSON() method in order to save it. Give the json snapshot to createRepository() to reload it:

const snapshot = repository.toJSON();
const newRepository = createRepository(snapshot);

Subscription

A repository exposes a subscribe and unsubscribe methods to be notified when a change occurs in the repository:

const subscriber = ({ head }) => console.log(`The new head of the repository is ${head}`);
repository.subscribe(subscriber);

const tree = {
    foo: 'bar',
};

// subscriber will be called with commitHash as head
const commitHash = repository.commit('robin', 'first commit', tree);

// you can unsubscribe at anytime
repository.unsubscribe(subscriber);

For example, if you want to persist your repository at each change into the local storage:

repository.subscribe(() => localStorage.setItem('repository', repository.toJSON()));

And when you load the repository:

const snapshot = JSON.parse(localStorage.getItem('repository'));
const repository = createRepository(snapshot);

API

  • repository.branch returns the current branch
  • repository.branches returns the list of branches
  • repository.head returns the head hash of the current branch
  • repository.log returns the full history of the repository
  • repository.tree returns the current tree of the repository
  • repository.apply(patch [, resolver]) applies a patch to the current tree and returns the result
  • repository.commit(author, message, tree) creates a new commit on the current branch
  • repository.checkout(branch [, create=false]) creates and/or changes the current branch
  • repository.deleteBranch(branch) removes a branch
  • repository.diff(left, right) generates a JSON Patch between two branches or commits
  • repository.merge(author, branch [, resolver]) merges a branch into the current branch
  • repository.revert(author, commitHash, [, resolver]) reverts the changes introduced by a commit
  • repository.subscribe(subscriber) subscribe a subscriber to the repository to be notified at each change
  • repository.toJSON() exports a snapshot of the repository
  • repository.unsubscribe(subscriber) unsubscribe a subscriber from the repository
  • createRepository([snapshot]) creates a new repository

Development

Install dependencies with yarn. You're then good to go.

To run the tests, just do npm test.

Contribute

All contributions are welcome and must pass the tests. If you add a new feature, please write tests for it.

License

This application is available under the MIT License.