Skip to content

Commit

Permalink
add unpack + test against a temporary git repo
Browse files Browse the repository at this point in the history
  • Loading branch information
tmpvar committed Nov 3, 2012
1 parent a562804 commit 74ec7ab
Show file tree
Hide file tree
Showing 9 changed files with 362 additions and 53 deletions.
10 changes: 10 additions & 0 deletions common/pack.js
@@ -0,0 +1,10 @@
module.exports.getType = function(typeStr) {
return {
"001":"commit",
"010":"tree",
"011":"blob",
"100":"tag",
"110":"ofs_delta",
"111":"ref_delta"
}[typeStr]
}
Empty file added lib/pack/index.js
Empty file.
24 changes: 24 additions & 0 deletions lib/pack/object.js
@@ -0,0 +1,24 @@
var PackObject = function(type, buffer, offset) {
this.type = type;
this.buffer = buffer;
this.offset = offset;
}

PackObject.prototype.type = null;


// createPackObject
module.exports.createPackObject = function(type, buffer, offset) {
return new PackObject(type, buffer, offset);
};

module.exports.OBJ_BAD = -1,
module.exports.OBJ_NONE = 0,
module.exports.OBJ_COMMIT = 1,
module.exports.OBJ_TREE = 2,
module.exports.OBJ_BLOB = 3,
module.exports.OBJ_TAG = 4,
module.exports.OBJ_OFS_DELTA = 6,
module.exports.OBJ_REF_DELTA = 7,
module.exports.OBJ_ANY,
module.exports.OBJ_MAX
102 changes: 102 additions & 0 deletions lib/unpack.js
@@ -0,0 +1,102 @@

// Unpack a git pack file
// see: http://www.kernel.org/pub/software/scm/git/docs/technical/pack-format.txt

var Buffer = require('buffer').Buffer,
packObject = require('./pack/object'),
getType = require('../common/pack').getType,
rawInflate = require('../common/rawinflate'),
Git = require('../git');;

// TODO: I really want to use this, but there is no way to get the
// number of bytes it consumed
//zlib = require('zlib').createInflate();

git = new Git();

module.exports = function unpack(buffer, fn) {

var obj = {
version: null,
count: null,
objects : []
},
where = 0,
fwd = function(amount) {
where += amount;
return where;
};

if (!Buffer.isBuffer(buffer)) {
return fn(new Error('Unpack expects a buffer'));
}

if (buffer.length < 4 || buffer.slice(0, 4).toString('ascii') !== 'PACK') {
return fn(new Error('Invalid signature'));
}

if (buffer.length < 8) {
return fn(new Error('Invalid version length'));
}

obj.version = buffer.readUInt32BE(4);
if (obj.version !== 2) {
return fn(new Error('Invalid version (expected 2)'));
}

if (buffer.length < 12) {
return fn(new Error('Invalid object count'));
}

obj.count = buffer.readUInt32BE(8);

if (obj.count > 0 && buffer.length === 12) {
return fn(new Error('Invalid object entry section'), obj)
}

var where = 12, objectIndex = 0;
var readObject = function(buffer) {
if (objectIndex < obj.count) {

// Read the type
var c = buffer.readUInt8(where);

var type = (c >> 4) & 7;
var length = (c & 15);
var shift = 4;
var offset = where;

fwd(1);
while (c & 0x80) {
c = buffer.readUInt8(where);
length += (c & 0x7f) << shift;
shift += 7;
fwd(1);
}

var inflated = rawInflate(git.bytesToString(buffer.slice(fwd(2))));
// TODO: WARNING!! the compressed length returned by this function is incorrect

// TODO: collect the sha
if (type === packObject.OBJ_OFS_DELTA || type === packObject.OBJ_REF_DELTA) {
fwd(20);
}

fwd(inflated.compressedLength + 4);

obj.objects.push(packObject.createPackObject(type, inflated.toString() , offset));

process.nextTick(function() {
objectIndex++;
readObject(buffer);
});

} else {
console.log(obj);
process.exit();
fn(null, obj);
}
};

readObject(buffer);
};
9 changes: 5 additions & 4 deletions package.json
Expand Up @@ -3,12 +3,13 @@
"description": "A subset of git features implemented in nodejs with streams",
"version": "0.0.1",
"dependencies": {
"request": "*",
"rimraf": "~2.0.2",
"glob": "~3.1.14"
"request": "*"
},
"devDependencies": {
"tap": "*"
"tap": "*",
"rimraf": "~2.0.2",
"glob": "~3.1.14",
"cpr": "0.0.6"
},
"contributors": [
{
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/repo-pack/readme.md
@@ -1 +1 @@
read this
hello world
44 changes: 28 additions & 16 deletions test/fixtures/repo.js
@@ -1,33 +1,45 @@
var fs = require('fs'),
exec = require('child_process').exec,
glob = require('glob'),
rimraf = require('rimraf');
rimraf = require('rimraf'),
cpr = require('cpr').cpr;

var packFile = __dirname + '/repo/.git/objects/pack/pack-82308c01d9ad8886e8c0003ddf5a83de8a60815f.pack';
var packRepoPath = __dirname + '/repo-pack/';

var packRepoPath = __dirname + '/tmp/repo-pack/';
var packRepoOpts = { cwd : packRepoPath };

module.exports.getPackFile = function(fn) {

exec('git init . && git add . && git commit -m "add readme" && git repack', packRepoOpts, function() {
exec('git count-objects -v', packRepoOpts, function(err, stdout) {
if (err) throw err;
cpr(__dirname + '/repo-pack/', packRepoPath, { deleteFirst: true }, function() {

var count = parseInt(stdout.split('\n').shift().split(' ').pop(), 10);
exec('git init . && git add . && git commit -m "add readme"', packRepoOpts, function() {

glob(packRepoPath + '/.git/objects/pack/*.pack', function(err, files) {
if (err) throw err;

var packFile = files[0];
fs.writeFileSync(packRepoPath + 'readme.md', "hello again");

fs.readFile(packFile, function(err, buffer) {
exec('git commit -am "change readme" && git repack && git verify-pack -v .git/objects/pack/pack-*.idx', packRepoOpts, function(e, out) {
console.log(out);
exec('git count-objects -v', packRepoOpts, function(err, stdout) {
if (err) throw err;

rimraf(packRepoPath + '/.git', function() {
fn(null, {
totalObjects: count,
buffer: buffer
});
var count = parseInt(stdout.split('\n').shift().split(' ').pop(), 10);

glob(packRepoPath + '/.git/objects/pack/*.pack', function(err, files) {
if (err) throw err;

var packFile = files[0];

fs.readFile(packFile, function(err, buffer) {
if (err) throw err;

// cleanup
//rimraf(packRepoPath, function() {
fn(null, {
totalObjects: count,
buffer: buffer
});
//});
});
});
});
});
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/tmp/repo-pack
Submodule repo-pack added at db9c8a

0 comments on commit 74ec7ab

Please sign in to comment.