Permalink
Browse files

cli - snob is now self hosting

  • Loading branch information...
1 parent 9fc30eb commit cf293e94bf09f7d2eaece7463c2fe8d888ec7514 @dominictarr committed Mar 18, 2012
Showing with 355 additions and 8 deletions.
  1. BIN .readme.markdown.swp
  2. +2 −0 .snob/commits
  3. +1 −0 .snob/state
  4. +181 −0 cli.js
  5. +3 −0 notes
  6. +21 −2 readme.markdown
  7. +17 −6 snob.js
  8. +63 −0 test/snob-test-branches.js
  9. +4 −0 test/snob-test.js
  10. +2 −0 test/test.js
  11. +61 −0 test/tree-test.js
View
Binary file not shown.
View

Large diffs are not rendered by default.

Oops, something went wrong.
View
@@ -0,0 +1 @@
+{"branches":{"master":"4f63d2637ae35e9313fd4f23ea6cf4e8e527ba3c"},"tags":{},"current":"master"}
View
181 cli.js
@@ -0,0 +1,181 @@
+var fs = require('fs')
+var join = require('path').join
+var Repo = require('./snob')
+
+// just for a joke, lets add a CLI so that snob can be self hosting.
+
+/*
+ COMMANDS
+
+ init // create a repo and persist it.
+ save commits in an append only log
+ save state in a json file
+ just needs branches,
+ and current checkout commit
+ commit file... save the current file in a new commit
+ checkout commitish (commitish = commit/tag/branch)
+ tag name commitish
+ merge commitish1 commitish2 || current_branch
+ branch branchname
+ whereami show current branch
+
+*/
+
+function Snob (dir) {
+ this.dir = dir
+ this.repo = new Repo()
+}
+
+Snob.prototype = {
+ read: function (file, ready) {
+ fs.readFile(join(this.dir, file), 'utf-8', ready)
+ },
+ load: function (callback) {
+ var repo = this.repo, self = this
+ var n = 2, err
+ function ready (e) {
+ err = err || e
+ if(--n) return
+ callback(err)
+ }
+
+ fs.readFile(join(this.dir, '.snob', 'commits'), 'utf-8',
+ function (err, commits) {
+ commits.split('\n').map(function (e) {
+ if(!e) return
+ var commit = JSON.parse(e)
+ repo.commits[commit.id] = commit
+ })
+ ready(err)
+ })
+
+ fs.readFile(join(this.dir, '.snob', 'state'), 'utf-8',
+ function (err, data) {
+ var state = JSON.parse(data)
+ repo.branches = state.branches || {}
+ repo.tags = state.tags || {}
+ self.current = state.current
+ console.log(state)
+ ready(err)
+ })
+ },
+ save: function (commit, callback) {
+ var n = 2, err
+ function ready (e) {
+ err = err || e
+ if(!--n) return
+ callback(err)
+ }
+ append(join(this.dir, '.snob', 'commits')
+ , JSON.stringify(commit) + '\n', ready)
+
+ fs.writeFile(join(this.dir, '.snob', 'state')
+ , JSON.stringify({
+ branches: this.repo.branches,
+ tags: this.repo.tags,
+ current: this.current || 'master' ,
+ }), ready)
+ }
+}
+
+function append (file, text, callback) {
+ var s = fs.createWriteStream(file, {flags: 'a'})
+ s.on('end', callback)
+ s.on('error', callback)
+ s.write(text)
+ s.end()
+}
+
+function findDir (dir) {
+ if(dir === '/')
+ throw new Error('is not a SNOB repo')
+ dir = dir || process.cwd()
+ var dotsnob = join(dir, '.snob')
+ try {
+ fs.readdirSync(dotsnob)
+ return new Snob(dir)
+ } catch (err) {
+ throw err // currently must use snob from root of repo
+ if(err.code === 'ENOENT')
+ return findDir(join(dir, '..'))
+ }
+}
+
+var commands = {
+ init: function (dir) {
+ dir = dir || process.cwd()
+ var dotsnob = join(dir, '.snob')
+ try {
+ fs.readdirSync(dotsnob)
+ } catch (err) {
+ //
+ if(err.code === 'ENOENT') {
+ // create
+ fs.mkdirSync(dotsnob)
+ fs.writeFileSync(join(dotsnob, 'commits'), '')
+ fs.writeFileSync(join(dotsnob, 'state'),
+ JSON.stringify({current: 'master'})
+ )
+ }
+ }
+ },
+ commit: function () {
+ var state = findDir()
+ var files = [].slice.call(arguments)
+ var n = files.length + 1// extra one is the repo commits file
+ var repo = state.repo
+ state.load (done)
+ if(!files.length)
+ throw new Error('expected args: files to commit')
+ console.log(files)
+ //read each file, and
+ var world = {}
+
+ files.map(function (f) {
+ state.read(f, function (err, text) {
+ world[f] = text.split('\n')
+ done(err)
+ })
+ })
+
+ function done (err, text) {
+ if(err) throw err
+ if(--n) return
+ var commit = repo.commit(world, {parent: state.current || 'master'})
+ console.log(commit)
+ state.save(commit, console.log)
+ }
+ },
+ heads: function () {
+ var state = findDir()
+ state.load(function () {
+ console.log(state.repo.heads())
+ })
+ },
+ branch: function () {
+ var state = findDir()
+ state.load(function (err) {
+ if(err) throw err
+ console.log(state.repo.branches, 'current:', state.current)
+ })
+ },
+ log: function () {
+ var state = findDir()
+ state.load(function () {
+ state.repo.revlist('master') // add changing branch
+ .map(function (id) {
+
+ var commit = state.repo.get(id)
+ console.log(commit.id, commit.parent, new Date(commit.timestamp))
+ })
+ })
+ }
+}
+
+var args = process.argv
+args.splice(0, 2)
+
+console.log(args)
+var cmd = args.shift()
+if(commands[cmd])
+ commands[cmd].apply(null, args)
View
3 notes
@@ -0,0 +1,3 @@
+[ZS97, Cha99, p. 17]. "Even simple variants of the unordered tree problem have been shown to be NP-hard
+ordered tree more computationally feasible, apparently!
+
View
@@ -8,7 +8,6 @@ one of the best uses is to learn you exactly how git works.
if you want to know what is the difference between two files, you must first know what is the same.
this is called the Longest Common Subsequence problem. if you have two sequences `x = "ABDCEF" and `y = "ABCXYZF"` then `LCS(x,y)` is clearly "ABCF".
-
## lcs
```
@@ -23,7 +22,7 @@ function lcs (a,b)
this is very simple, but with exponential time complexity.
however, it can easily be made sufficantly performant by cacheing the return value of each call to lcs().
-see js implementation, [index.js#L64-94](index.js#L64-94)
+see js implementation, [index.js#L64-94](https://github.com/dominictarr/adiff/blob/master/index.js#L63-94)
## chunking
@@ -76,3 +75,23 @@ this is only a slightly more complicated problem. given a string `"ABDCEF"`, If
and meanwhile you changed it to "AXBCEFG". we must compare each of our changes to the original string, the [Concestor](http://en.wikipedia.org/wiki/Concestor)
TODO: worked example with chunks, resolve.
+
+## self hosting!
+
+snob became self hosting
+
+```
+4f63d2637ae35e9313fd4f23ea6cf4e8e527ba3c null Sun, 18 Mar 2012 08:20:34 GMT
+```
+
+
+## what next?
+
+refactor snob out of adiff.
+still thinking about how I will manage diffs on objects,
+ - refactor vu, with what I know now.
+giving snob a cli, and in particular, push/pullable remotes, is the first step to being distributed.
+
+ - improve support for injection with merge resolution.
+ - get running in the browser
+ - make Repository an EventEmitter
View
23 snob.js
@@ -47,13 +47,16 @@ Repository.prototype = {
commit.timestamp = Date.now()
commit.id = hash(commit)
+ // XXX make an error if the commits are empty !!! XXX
+
this.commits[commit.id] = commit
this.branch(branch, commit.id)
+ console.log('new commit',branch, commit.id, this.getId(commit.id))
return commit
// emit the new commit
},
get: function (commitish) {
- return this.commits[commitish] || this.branches[commitish] || this.tags[commitish]
+ return this.commits[commitish] || this.commits[this.branches[commitish] || this.tags[commitish]]
},
getId: function (commitish) {
return (this.get(commitish) || {id:null}).id
@@ -63,18 +66,24 @@ Repository.prototype = {
},
branch: function (name, commitish) {
// do not save this as a branch if it's actually a commit, or a tag.
- if(this.commits[name] || this.tags[name]) return
- this.branches[name] = this.getId(commitish)
+ if(this.commits[name] || this.tags[name]) {
+ console.log('!!!!!!!!!!!!!!!!!!!!!')
+ return this.getId(commitish)
+ }
+ console.log('BRANCH', name, commitish)
+ return this.branches[name] = this.getId(commitish)
},
diff: function (parent, world) {
var head = this.checkout(parent)
+ if('object' !== typeof world)
+ world = this.checkout(world)
return map(world, function (b, f) {
return a.diff(head[f] || [], b)
})
},
revlist: function (id) {
- if(!id) return []
- var commit = this.commits[id]
+ var commit = this.get(id)
+ if(!commit) return []
return this.revlist(commit.parent).concat(id)
},
concestor: function (heads) { //a list of commits you want to merge
@@ -129,9 +138,11 @@ Repository.prototype = {
commit.merged = branches
commit.parent = branches[0]
+ commit.depth = this.commits[branches[0]].depth + 1
commit.timestamp = Date.now()
+
commit.id = hash(commit)
- commit.depth = this.commits[branches[0]].depth + 1
+
this.commits[commit.id] = commit
this.branch(mine, commit.id) // if this was merge( ['master', ...], ...) update the branch
return commit
View
@@ -0,0 +1,63 @@
+
+var assert = require('assert')
+var Repo = require('../snob')
+
+var snob = new Repo()
+var world
+var init = snob.commit(world = {
+ hello: ['who', 'what', 'when','why']
+ },
+ { message: 'init', parent: 'master'})
+
+assert.equal(init.depth, 1)
+
+assert.equal(snob.getId('master'), init.id)
+
+snob.branch('underdog', init.id)
+
+var _world = snob.checkout(init.id)
+
+assert.deepEqual(_world, world)
+
+world.hello.splice(2, 0, 'how')
+
+var second = snob.commit(world, {
+ message: 'second',
+ parent: 'master'
+ })
+
+console.log(second)
+console.log(snob.revlist(second.id))
+
+assert.deepEqual(
+ snob.revlist(second.id),
+ [ init.id, second.id])
+
+assert.deepEqual(second.depth, 2)
+
+//a branch off init, so can test merge
+
+_world.hello.push('WTF!?')
+var branch = snob.commit(_world, {
+ message: 'branch',
+ parent: 'underdog'
+ })
+
+assert.equal(branch.depth, 2)
+console.log(branch)
+
+var concestor = snob.concestor(branch.id, second.id)
+
+assert.equal(concestor, init.id)
+console.log(concestor)
+
+var merged = snob.merge([branch.id, second.id], {message: 'merge1'})
+
+console.log(merged)
+assert.equal(3, merged.depth)
+
+var world3 = snob.checkout(merged.id)
+
+console.log(world3)
+
+
View
@@ -40,9 +40,12 @@ var branch = snob.commit(_world, {
assert.equal(branch.depth, 2)
console.log(branch)
+
var concestor = snob.concestor(branch.id, second.id)
+
assert.equal(concestor, init.id)
console.log(concestor)
+
var merged = snob.merge([branch.id, second.id], {message: 'merge1'})
console.log(merged)
@@ -52,3 +55,4 @@ var world3 = snob.checkout(merged.id)
console.log(world3)
+
View
@@ -62,4 +62,6 @@ if(!module.parent) {
//note, it's possible for this case to occur in a
//n-way merge where there is a delete and a false conflict.
//most merges will be only 3 ways, so lets leave that for now.
+
+ console.log(d.diff3(split("ABCXYZF"), split("ABDCEF"), split("AXBCEFG")))
}
Oops, something went wrong.

0 comments on commit cf293e9

Please sign in to comment.