├── .gitignore ├── README.md ├── package.json └── src ├── command ├── checkout.js ├── clone.js ├── diff.js ├── index.js ├── init.js ├── log.js ├── ls-files.js ├── ls-tree.js ├── show.js └── status.js ├── index.js ├── parse ├── file-tree-parser.js └── status-parser.js └── runner.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | debug.log 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Execute Git Command by Node.js 2 | 3 | ## How to use it 4 | 5 | ```js 6 | var Git = require('git-command'); 7 | var git = new Git(YOUR_REPO_PATH); 8 | 9 | // For example, git log 10 | 11 | git.log().then(function(result){ 12 | 13 | }); 14 | 15 | ``` 16 | 17 | DONE 18 | 19 | * git status 20 | * git log 21 | * git ls-files 22 | * git diff 23 | * git clone 24 | * git checkout 25 | 26 | TODO 27 | * git show 28 | 29 | 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "git-command", 3 | "version": "1.0.10", 4 | "description": "Execute git command using Node.js", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "run": "node ./src/runner.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/liangshuai/node-git.git" 13 | }, 14 | "keywords": [ 15 | "Git" 16 | ], 17 | "author": "ShuaiLiang", 18 | "license": "ISC", 19 | "dependencies": { 20 | "abs": "^1.3.4", 21 | "diff2html": "^1.3.2", 22 | "q": "1.4.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/command/checkout.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var runner = require('../runner'); 3 | 4 | module.exports = function(targetCommitID) { 5 | return runner.execute(['git', 'checkout', targetCommitID]); 6 | }; -------------------------------------------------------------------------------- /src/command/clone.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var runner = require('../runner'); 3 | 4 | module.exports = function(url) { 5 | return runner.execute(['git', 'clone', url]); 6 | } -------------------------------------------------------------------------------- /src/command/diff.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var runner = require('../runner'); 3 | var diff2Html = require('diff2html').Diff2Html; 4 | var summaryParser = require('../parse/status-parser'); 5 | // var parse = function(diff) { 6 | 7 | // var filename; 8 | // var isEmpty = true; 9 | // var files = {}; 10 | 11 | // diff.split( "\n" ).forEach(function( line, i ) { 12 | // if ( !line || line.charAt( 0 ) === "*" ) { 13 | // return; 14 | // } 15 | 16 | // if ( line.charAt( 0 ) === "d" ) { 17 | // isEmpty = false; 18 | // filename = line.replace( /^diff --(?:cc |git a\/)(\S+).*$/, "$1" ); 19 | // files[ filename ] = []; 20 | // } 21 | 22 | // files[ filename ].push( line ); 23 | // }); 24 | 25 | // return isEmpty ? null : files; 26 | // } 27 | 28 | 29 | module.exports = function(commitA, commitB, mode) { 30 | var parser = mode === 's' ? summaryParser : diff2Html.getJsonFromDiff; 31 | var command = ['git', 'diff', commitA, commitB]; 32 | mode === 's' && command.push('--name-status'); 33 | 34 | return runner.execute(command, parser); 35 | } -------------------------------------------------------------------------------- /src/command/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var status = require('./status'); 4 | var log = require('./log'); 5 | var files = require('./ls-files'); 6 | var tree = require('./ls-tree'); 7 | var diff = require('./diff'); 8 | var clone = require('./clone'); 9 | var show = require('./show'); 10 | var checkout = require('./checkout'); 11 | 12 | module.exports.status = status; 13 | module.exports.log = log; 14 | module.exports.files = files; 15 | module.exports.tree = tree; 16 | module.exports.diff = diff; 17 | module.exports.clone = clone; 18 | module.exports.show = show; 19 | module.exports.checkout = checkout; 20 | -------------------------------------------------------------------------------- /src/command/init.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(runner) { 4 | 5 | } -------------------------------------------------------------------------------- /src/command/log.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var runner = require('../runner'); 3 | 4 | // TODO: still not test 5 | var Log = function(symbol) { 6 | var logMsg = symbol.split('\n'), 7 | BODY_START_LINE_INDEX = 8, 8 | attrIndex = 0, 9 | bodyParser = function(lines) { 10 | var bodyLines = lines.slice(BODY_START_LINE_INDEX), 11 | bodyFullText = bodyLines.join('\n').replace(/\n+$/g, ''), 12 | bodyValidText = bodyFullText.substring(1, bodyFullText.length - 1); 13 | 14 | return bodyValidText; 15 | }; 16 | 17 | return { 18 | 'commitId': logMsg[attrIndex++], 19 | 'author': logMsg[attrIndex++], 20 | 'authorEmail': logMsg[attrIndex++], 21 | 'committer': logMsg[attrIndex++], 22 | 'committerEmail': logMsg[attrIndex++], 23 | 'commitDate': logMsg[attrIndex++], 24 | 'commitRelateDate': logMsg[attrIndex++], 25 | 'commitSubject': logMsg[attrIndex++], 26 | 'commitBody': bodyParser(logMsg) // need cobime left lines 27 | }; 28 | }; 29 | 30 | 31 | function parse(result) { 32 | var allLogsMsg = result.split('\n\n\n'), 33 | logs = [], 34 | logMsg; 35 | 36 | allLogsMsg.map(function(logMsg) { 37 | logs.push(Log(logMsg)); 38 | }); 39 | return logs; 40 | } 41 | 42 | module.exports = function(logSource) { 43 | var needOutputFormat = [ 44 | // commit hash 45 | '%H', 46 | // author name 47 | '%an', 48 | // author email 49 | '%ae', 50 | // committer name 51 | '%cn', 52 | // committer email 53 | '%ce', 54 | // committer date 55 | '%cd', 56 | // committer date, relative 57 | '%cr', 58 | // commit subject 59 | '%s', 60 | // commit body 61 | ':%b:', 62 | // last end line 63 | '%n' 64 | ]; 65 | 66 | logSource = typeof logSource === 'string' ? logSource : ''; 67 | 68 | return runner.execute(['git', 'log', logSource, 69 | '--pretty=format:' + needOutputFormat.join('%n') + ''], 70 | parse); 71 | }; -------------------------------------------------------------------------------- /src/command/ls-files.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var runner = require('../runner'); 3 | var parse = require('../parse/file-tree-parser'); 4 | 5 | module.exports = function() { 6 | return runner.execute(['git', 'ls-files'], parse); 7 | } -------------------------------------------------------------------------------- /src/command/ls-tree.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var runner = require('../runner'); 3 | var parse = require('../parse/file-tree-parser'); 4 | 5 | module.exports = function(commitID) { 6 | return runner.execute(['git', 'ls-tree', '--name-only', '-r', commitID], parse); 7 | } -------------------------------------------------------------------------------- /src/command/show.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var runner = require('../runner'); 3 | 4 | module.exports = function(commitID, file) { 5 | return runner.execute(['git', 'show', commitID + ':' + file]); 6 | } -------------------------------------------------------------------------------- /src/command/status.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var runner = require('../runner'); 3 | var parser = require('../parse/status-parser'); 4 | 5 | 6 | module.exports = function() { 7 | return runner.execute(['git', 'status', '-s'], parser); 8 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var runner = require('./runner'); 4 | var command = require('./command'); 5 | var Git = function(baseDir) { 6 | runner.baseDir = baseDir; 7 | }; 8 | 9 | Git.prototype = command; 10 | Git.prototype.constructor = Git; 11 | Git.prototype.setBaseDir = function(baseDir) { 12 | runner.baseDir = baseDir; 13 | return this; 14 | } 15 | 16 | module.exports = Git; -------------------------------------------------------------------------------- /src/parse/file-tree-parser.js: -------------------------------------------------------------------------------- 1 | var parse = function(output) { 2 | var pathArr = output.trim().split('\n'); 3 | var forEach = [].forEach; 4 | var result = {}; 5 | 6 | forEach.call(pathArr, function(item) { 7 | var itemArr = item.trim().split('/'); 8 | var len = itemArr.length; 9 | itemArr.reduce(function(prev, curr, index) { 10 | if(index === len - 1) { 11 | prev['files'] || (prev['files'] = []); 12 | prev['files'].push(curr); 13 | return prev; 14 | } 15 | if(!prev[curr]){ 16 | prev[curr] = {}; 17 | } 18 | return prev[curr]; 19 | }, result); 20 | }); 21 | return result; 22 | 23 | } 24 | 25 | module.exports = parse; -------------------------------------------------------------------------------- /src/parse/status-parser.js: -------------------------------------------------------------------------------- 1 | var Status = function(symbol) { 2 | var mapping = { 3 | '??': 'notAdded', 4 | 'M': 'modified', 5 | 'D': 'deleted', 6 | 'A': 'created', 7 | 'UU': 'conflicted', 8 | 'AM': 'createdAndModified', 9 | 'MM': 'stagedAndModified' 10 | }, 11 | current = mapping[symbol]; 12 | 13 | return function(files, result) { 14 | if(!result[current]){ 15 | result[current] = []; 16 | } 17 | result[current].push(files); 18 | } 19 | } 20 | module.exports = function(result) { 21 | var lines = result.trim().split('\n'); 22 | var line = ''; 23 | var handler; 24 | var result = {}; 25 | while (line = lines.shift()) { 26 | line = line.trim().match(/(\S+)\s+(.*)/); 27 | if(line && (handler = Status([line[1]]))) { 28 | handler(line[2], result); 29 | } 30 | } 31 | return result; 32 | } -------------------------------------------------------------------------------- /src/runner.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var childproc = require('child_process'), 4 | abs = require('abs'), 5 | exec = childproc.exec, 6 | execSync = childproc.execSync, 7 | spawn = childproc.spawn, 8 | Runner, 9 | noop, 10 | Q = require('q'); 11 | 12 | Runner = function(){ 13 | this.baseDir = './'; 14 | }; 15 | 16 | noop = function(result){ 17 | return result; 18 | }; 19 | 20 | Runner.prototype.execute = function(command, parse, options) { 21 | var task = command.shift(); 22 | var defer = Q.defer(); 23 | var result = ""; 24 | var executor = spawn(task, command, { 25 | cwd: abs(this.baseDir) 26 | }); 27 | parse = parse || noop; 28 | executor.stdout.on('data', function (data) { 29 | result += data; 30 | }); 31 | executor.stderr.on('data', function (data) { 32 | console.log('Error:\n' + data); 33 | }); 34 | 35 | executor.on('close', function (code, signal) { 36 | if (code === 0) { 37 | defer.resolve(parse(result)); 38 | } else { 39 | defer.reject(new Error(code)); 40 | } 41 | }); 42 | return defer.promise; 43 | } 44 | var runner = new Runner(); 45 | 46 | module.exports = runner; 47 | 48 | 49 | --------------------------------------------------------------------------------