commit e5108dc86261ca9c7902d7740c04c3648a4cb39a
parent b126e1dcbf5895404a894d63565d775583ef932d
Author: Remy Noulin (Spartatek) <remy.noulin@spartatek.se>
Date: Tue, 22 Nov 2016 15:38:16 +0100
Fix npm package
and simplify ssh configuration
README.md | 1 +
bin/git-off | 1 -
gen.sh | 6 +
package.json | 4 +-
src/git-off | 748 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/gitoff.coffee | 103 +++++---
6 files changed, 831 insertions(+), 32 deletions(-)
Diffstat:
6 files changed, 831 insertions(+), 33 deletions(-)
diff --git a/README.md b/README.md
@@ -106,5 +106,6 @@ git off help
# Dependencies
+- nodejs
- git
- ssh and scp for scp transport
diff --git a/bin/git-off b/bin/git-off
@@ -1 +0,0 @@
-../src/gitoff.coffee-
\ No newline at end of file
diff --git a/gen.sh b/gen.sh
@@ -0,0 +1,6 @@
+#! /usr/bin/env bash
+
+NAME=git-off
+echo "#! /usr/bin/env node" > src/$NAME
+coffee --compile --print --bare --no-header src/gitoff.coffee >> src/$NAME
+chmod 755 src/$NAME
diff --git a/package.json b/package.json
@@ -1,8 +1,8 @@
{
"name": "git-off",
- "version": "0.0.1",
+ "version": "0.0.2",
"description": "large file handler for git",
- "bin": "./bin/git-off",
+ "bin": "./src/git-off",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
diff --git a/src/git-off b/src/git-off
@@ -0,0 +1,748 @@
+#! /usr/bin/env node
+/*
+ * USAGE
+ *
+ * COMMANDS
+ *
+ * git off install [thisrepo]
+ * setup git config (default global)
+ * thisrepo sets up config in current repo
+ *
+ * git off mode [thisrepo] [copy|scp]
+ * set/show git off mode
+ *
+ * git off scp [thisrepo] [host]
+ * setup scp config
+ * host has format host:path
+ * Example: localhost:/tmp/offStore
+ *
+ * git off scpuser [thisrepo] [username]
+ * setup scp username config
+ *
+ * git off track
+ * setup gitattribute filters
+ * example: git off track '*.bin'
+ * without parameter, list git off attributes
+ * calls git off install
+ *
+ * git off clean
+ * internal filter
+ *
+ * git off smudge
+ * internal filter
+ *
+ * git off pre-push
+ * internal filter
+ *
+ * git off smudge
+ * internal filter
+ *
+ * git off clearAll
+ * git off ca
+ * delete store, cache and log
+ *
+ * git off clearCache
+ * git off cc
+ * deletes cache in current git
+ *
+ * git off defaults
+ * shows first time config
+ *
+ * git off env
+ * shows config
+ *
+ * git off help
+ * shows git off help
+ *
+ * config:
+ * gitoff mode rsync, http, copy
+ * store=/we
+ */
+
+/*
+ * CODE
+ *
+ * modules
+ * defaults
+ * helpers
+ * core
+ * main
+ *
+ *
+ * modules
+ * all dependencies
+ *
+ * defaults
+ * objInfo fields: for indexing cat-diff response in push command
+ * externalHelpers: shell commands to be used with the exec function
+ * runtimeConfig: config built by offHelpers
+ * offDEFAULTS: default configuration for first time install
+ *
+ * helpers
+ * expandHome: expands ~/
+ * gitConfig: handles global git config
+ * offLog: appends log to git config off.log
+ * offLogRepo: log for commands run in git repo
+ * exec: runs an externalHelpers with parameters
+ * mkdirParents: recursive mkdir
+ * rmAll: delete recursively files and directories
+ * copy: copies files
+ * offHelpers: git-off helpers
+ *
+ * core
+ * transport: transport functions for git off store
+ * offCommands: command line functions
+ *
+ * main
+ * parse CLI arguments
+ */
+
+/*
+ * modules
+ */
+var StringDecoder, copy, exec, expandHome, externalHelpers, fs, gitConfig, mkdirParents, mkdirp, offCommands, offDEFAULTS, offHelpers, offLog, offLogRepo, oiNAME, oiOID, oiPERMISSIONS, oiPREVIOUSOID, oiPREVIOUSPERMISSIONS, readline, rimraf, rmAll, runtimeConfig, syncexec, thisrepo, transport;
+
+require('colors');
+
+fs = require('fs');
+
+syncexec = require('sync-exec');
+
+mkdirp = require('mkdirp');
+
+rimraf = require('rimraf');
+
+readline = require('readline');
+
+StringDecoder = require('string_decoder').StringDecoder;
+
+
+/*
+ * defaults
+ */
+
+oiPREVIOUSPERMISSIONS = 0;
+
+oiPERMISSIONS = 1;
+
+oiPREVIOUSOID = 2;
+
+oiOID = 3;
+
+oiNAME = 4;
+
+externalHelpers = {
+ 'gitConfigGlobal': 'git config --global',
+ 'gitConfig': 'git config',
+ 'gitRepoRoot': 'git rev-parse --show-toplevel',
+ 'gitList': 'git rev-list',
+ 'gitDiff': 'git diff-tree -r',
+ 'gitCat': 'git cat-file -p',
+ 'sha': 'git hash-object --no-filters',
+ 'listAttr': 'git check-attr -a',
+ 'ssh': 'ssh -C -o StrictHostKeyChecking=no -o ConnectTimeout=3',
+ 'scp': 'scp -C -o StrictHostKeyChecking=no -o ConnectTimeout=3 -p'
+};
+
+runtimeConfig = {
+ 'currentRepoRoot': '',
+ 'objectPath': '',
+ 'offStore': '',
+ 'offMode': '',
+ 'offScp': '',
+ 'offScpUser': '',
+ 'log': ''
+};
+
+offDEFAULTS = {
+ 'objectPath': '/.git/off/objects',
+ 'mode': 'copy',
+ 'store': '~/.git-off/offStore',
+ 'log': '~/.git-off/log',
+ 'prePush': '#!/bin/sh\ncommand -v git-off >/dev/null 2>&1 || { echo >&2 "\\nThis repository is configured for Git off but \'git-off\' was not found on your path. If you no longer wish to use git off, remove this hook by deleting .git/hooks/pre-push.\\n"; exit 2; }\ngit off pre-push "$@"',
+ 'offSignature': '### git-off v1 sha:',
+ 'shaLength': 40
+};
+
+
+/*
+ * helpers
+ */
+
+expandHome = function(path) {
+ if (path.slice(0, 2) === '~/') {
+ path = process.env.HOME + '/' + path.slice(2);
+ }
+ return path;
+};
+
+gitConfig = {
+ 'set': function(key, value) {
+ exec('gitConfig', ['--global', key, '"' + value + '"']);
+ },
+ 'setLocal': function(key, value) {
+ exec('gitConfig', [key, '"' + value + '"']);
+ },
+ 'setThisRepo': function(key, value) {
+ exec('gitConfig', ['--file ' + offHelpers.gitRepoRoot() + '/.git-off', key, '"' + value + '"']);
+ },
+ 'get': function(key) {
+ if (fs.existsSync(offHelpers.gitRepoRoot(true) + '/.git-off') === false) {
+ return exec('gitConfig', [key]).stdout.trim();
+ } else {
+ return exec('gitConfig', ['--file ' + offHelpers.gitRepoRoot() + '/.git-off', key]).stdout.trim();
+ }
+ },
+ 'getSyncexec': function(key) {
+ return syncexec(externalHelpers.gitConfig + ' ' + key);
+ }
+};
+
+offLog = function(s) {
+ var r;
+ if (runtimeConfig.log === '') {
+ r = gitConfig.getSyncexec('off.log');
+ runtimeConfig.log = expandHome(r.stdout.trim());
+ if (runtimeConfig.log === '') {
+ console.error('Missing off.log config. Run "git config --global off.log ~/.git-off/log".'.red.bold);
+ process.exit(1);
+ }
+ }
+ fs.appendFileSync(runtimeConfig.log, s + '\n');
+};
+
+offLogRepo = function(s) {
+ offLog('REPO path: ' + offHelpers.gitRepoRoot() + ' ' + s);
+};
+
+exec = function(cmdName, paramsArray, ignoreStderr) {
+ var r;
+ if (paramsArray == null) {
+ paramsArray = [];
+ }
+ if (ignoreStderr == null) {
+ ignoreStderr = false;
+ }
+ r = syncexec(externalHelpers[cmdName] + ' ' + paramsArray.join(' '));
+ if (r.stderr !== '' && ignoreStderr === false) {
+ console.error(r.stderr.red.bold);
+ offLog(r.stderr.red.bold);
+ process.exit(1);
+ }
+ return r;
+};
+
+mkdirParents = function(path) {
+ path = expandHome(path);
+ mkdirp.sync(path, function(err) {
+ console.error(err);
+ });
+};
+
+rmAll = function(path) {
+ path = expandHome(path);
+ rimraf(path, function(err) {});
+};
+
+copy = function(src, dst) {
+ src = expandHome(src);
+ dst = expandHome(dst);
+ fs.createReadStream(src).pipe(fs.createWriteStream(dst));
+};
+
+offHelpers = {
+ 'gitRepoRoot': function(ignoreStderr) {
+ var r;
+ if (ignoreStderr == null) {
+ ignoreStderr = false;
+ }
+ if (runtimeConfig.currentRepoRoot === '') {
+ r = exec('gitRepoRoot', [], ignoreStderr);
+ runtimeConfig.currentRepoRoot = r.stdout.trim();
+ }
+ return runtimeConfig.currentRepoRoot;
+ },
+ 'objectPath': function() {
+ if (runtimeConfig.objectPath === '') {
+ runtimeConfig.objectPath = this.gitRepoRoot() + offDEFAULTS.objectPath;
+ }
+ return runtimeConfig.objectPath;
+ },
+ 'offStore': function() {
+ var r;
+ if (runtimeConfig.offStore === '') {
+ r = gitConfig.get('off.store');
+ runtimeConfig.offStore = expandHome(r);
+ }
+ return runtimeConfig.offStore;
+ },
+ 'offMode': function() {
+ var r;
+ if (runtimeConfig.offMode === '') {
+ r = gitConfig.get('off.mode');
+ runtimeConfig.offMode = expandHome(r);
+ }
+ return runtimeConfig.offMode;
+ },
+ 'offScp': function() {
+ if (runtimeConfig.offScp === '') {
+ runtimeConfig.offScp = gitConfig.get('off.scphost');
+ }
+ return runtimeConfig.offScp;
+ },
+ 'offScpUser': function() {
+ var r;
+ if (runtimeConfig.offScpUser === '') {
+ r = gitConfig.get('off.scpuser');
+ runtimeConfig.offScpUser = expandHome(r);
+ }
+ return runtimeConfig.offScpUser;
+ },
+ 'log': function() {
+ var r;
+ if (runtimeConfig.log === '') {
+ r = gitConfig.getSyncexec('off.log');
+ runtimeConfig.log = expandHome(r.stdout.trim());
+ }
+ return runtimeConfig.log;
+ },
+ 'getLog': function() {
+ offHelpers.log();
+ if (runtimeConfig.log === '') {
+ console.error('Missing off.log config. Run "git config --global off.log ~/.git-off/log".'.red.bold);
+ process.exit(1);
+ }
+ return runtimeConfig.log;
+ },
+ 'userAt': function() {
+ var user;
+ if (offHelpers.offScpUser() !== '') {
+ user = offHelpers.offScpUser() + '@';
+ } else {
+ user = '';
+ }
+ return user;
+ },
+ 'getSSHConfig': function() {
+ var h_l, host, port, portAndPath_l, storePath, user;
+ user = offHelpers.userAt();
+ h_l = offHelpers.offScp().split(':');
+ portAndPath_l = h_l[1].split('/');
+ port = parseInt(portAndPath_l[0]);
+ if (port === NaN) {
+ host = h_l[0];
+ storePath = h_l[1];
+ } else {
+ host = h_l[0];
+ storePath = '/' + portAndPath_l.slice(1).join('/');
+ }
+ return [user + host, storePath, port];
+ },
+ 'mkdirStore': function(path) {
+ var h_l, sshCmd;
+ h_l = offHelpers.getSSHConfig();
+ sshCmd = '"mkdir -p ' + h_l[1] + '/' + path;
+ sshCmd += '"';
+ if (h_l[2] === NaN) {
+ exec('ssh', [h_l[0], sshCmd], true);
+ } else {
+ exec('ssh', ['-p ' + h_l[2], h_l[0], sshCmd], true);
+ }
+ },
+ 'rmAllStore': function(path) {
+ var h_l, sshCmd;
+ h_l = offHelpers.getSSHConfig();
+ sshCmd = '"rm -rf ' + h_l[1] + '/' + path;
+ sshCmd += '"';
+ if (h_l[2] === NaN) {
+ exec('ssh', [h_l[0], sshCmd], true);
+ } else {
+ exec('ssh', ['-p ' + h_l[2], h_l[0], sshCmd], true);
+ }
+ },
+ 'setTransport': function() {
+ if (offHelpers.offMode() === 'copy') {
+ transport['send'] = function(file) {
+ var f_l;
+ f_l = file.split('/');
+ if (fs.existsSync(offHelpers.offStore() + '/' + f_l[0] + '/' + f_l[1]) === false) {
+ mkdirParents(offHelpers.offStore() + '/' + f_l[0] + '/' + f_l[1]);
+ }
+ copy(offHelpers.objectPath() + '/' + file, offHelpers.offStore() + '/' + file);
+ fs.chmodSync(offHelpers.offStore() + '/' + file, '444');
+ };
+ transport['receive'] = function(file) {
+ var readStream;
+ readStream = fs.createReadStream(offHelpers.offStore() + '/' + file);
+ readStream.pipe(process.stdout);
+ };
+ } else if (offHelpers.offMode() === 'scp') {
+ transport['send'] = function(file) {
+ var f_l, h_l, user;
+ user = offHelpers.userAt();
+ f_l = file.split('/');
+ offHelpers.mkdirStore(f_l[0] + '/' + f_l[1]);
+ h_l = offHelpers.getSSHConfig();
+ if (h_l[2] === NaN) {
+ exec('scp', [offHelpers.objectPath() + '/' + file, user + offHelpers.offScp() + '/' + file]);
+ } else {
+ exec('scp', ['-P ' + h_l[2], offHelpers.objectPath() + '/' + file, h_l[0] + ':' + h_l[1] + '/' + file]);
+ }
+ };
+ transport['receive'] = function(file) {
+ var f_l, h_l, readStream, user;
+ f_l = file.split('/');
+ if (fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) === false) {
+ mkdirParents(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]);
+ }
+ user = offHelpers.userAt();
+ h_l = offHelpers.getSSHConfig();
+ if (h_l[2] === NaN) {
+ exec('scp', [user + offHelpers.offScp() + '/' + file, offHelpers.objectPath() + '/' + file]);
+ } else {
+ exec('scp', ['-P ' + h_l[2], h_l[0] + ':' + h_l[1] + '/' + file, offHelpers.objectPath() + '/' + file]);
+ }
+ readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file);
+ readStream.pipe(process.stdout);
+ };
+ }
+ },
+ 'getOffFilePath': function(offFile) {
+ return [offFile.slice(0, 2) + '/' + offFile.slice(2, 4) + '/' + offFile, offFile.slice(0, 2) + '/' + offFile.slice(2, 4)];
+ }
+};
+
+
+/*
+ * core
+ */
+
+transport = {
+ 'send': function(src) {},
+ 'receive': function(src) {}
+};
+
+offCommands = {
+ 'localSetup': function() {
+ var hook;
+ mkdirParents(offHelpers.objectPath());
+ hook = offHelpers.gitRepoRoot() + '/.git/hooks/pre-push';
+ if (fs.existsSync(hook) === false) {
+ fs.writeFileSync(hook, offDEFAULTS.prePush);
+ fs.chmodSync(hook, '755');
+ }
+ },
+ 'install': function(setCfg) {
+ if (setCfg == null) {
+ setCfg = gitConfig.set;
+ }
+ offCommands.localSetup();
+ if (gitConfig.get('filter.off.clean') === '' || setCfg !== gitConfig.set) {
+ if (setCfg === gitConfig.setThisRepo) {
+ gitConfig.setLocal('filter.off.clean', 'git off clean %f');
+ } else {
+ setCfg('filter.off.clean', 'git off clean %f');
+ }
+ }
+ if (gitConfig.get('filter.off.smudge') === '' || setCfg !== gitConfig.set) {
+ if (setCfg === gitConfig.setThisRepo) {
+ gitConfig.setLocal('filter.off.smudge', 'git off smudge %f');
+ } else {
+ setCfg('filter.off.smudge', 'git off smudge %f');
+ }
+ }
+ if (offHelpers.log() === '' || setCfg !== gitConfig.set) {
+ if (setCfg === gitConfig.setThisRepo) {
+ gitConfig.setLocal('off.log', offDEFAULTS.log);
+ } else {
+ setCfg('off.log', offDEFAULTS.log);
+ }
+ }
+ if (offHelpers.offMode() === '' || setCfg !== gitConfig.set) {
+ setCfg('off.mode', offDEFAULTS.mode);
+ }
+ if (offHelpers.offStore() === '' || setCfg !== gitConfig.set) {
+ setCfg('off.store', offDEFAULTS.store);
+ }
+ if (runtimeConfig.offMode === 'copy') {
+ mkdirParents(runtimeConfig.offStore);
+ }
+ if (runtimeConfig.offMode === 'scp') {
+ offHelpers.mkdirStore('');
+ }
+ },
+ 'mode': function(setCfg) {
+ var len, mode;
+ len = process.argv.length;
+ mode = process.argv[len - 1];
+ if (mode !== 'mode' && mode !== 'thisrepo') {
+ setCfg('off.mode', mode);
+ } else {
+ console.log('off.mode '.blue.bold + offHelpers.offMode());
+ }
+ },
+ 'scp': function(setCfg) {
+ var len, scpHost;
+ len = process.argv.length;
+ scpHost = process.argv[len - 1];
+ if (scpHost !== 'scp' && scpHost !== 'thisrepo') {
+ setCfg('off.scphost', scpHost);
+ } else {
+ console.log('off.scp '.blue.bold + offHelpers.offScp());
+ }
+ },
+ 'scpUser': function(setCfg) {
+ var len, scpUser;
+ len = process.argv.length;
+ scpUser = process.argv[len - 1];
+ if (scpUser !== 'scpuser' && scpUser !== 'thisrepo') {
+ setCfg('off.scpuser', scpUser);
+ } else {
+ console.log('off.scpuser '.blue.bold + offHelpers.offScpUser());
+ }
+ },
+ 'track': function() {
+ var i, l, len1, r, ref;
+ if (process.argv[3] !== void 0) {
+ offCommands.install();
+ fs.appendFileSync(offHelpers.gitRepoRoot() + '/.gitattributes', process.argv[3] + ' filter=off -text\n');
+ } else {
+ r = exec('listAttr', ['`cd ' + offHelpers.gitRepoRoot() + '; git ls-files`']);
+ ref = r.stdout.split('\n');
+ for (i = 0, len1 = ref.length; i < len1; i++) {
+ l = ref[i];
+ if (l.indexOf(': filter: off') !== -1) {
+ console.log(l);
+ }
+ }
+ }
+ },
+ 'clean': function() {
+ var file, offFile, offFilePath, r, size, writeStream;
+ offCommands.localSetup();
+ file = process.argv[3];
+ size = fs.statSync(file).size;
+ r = exec('sha', [file]);
+ offFile = r.stdout.split(' ')[0].trim();
+ offFilePath = offHelpers.getOffFilePath(offFile);
+ if (fs.existsSync(offHelpers.objectPath() + '/' + offFilePath[1]) === false) {
+ mkdirParents(offHelpers.objectPath() + '/' + offFilePath[1]);
+ }
+ offFilePath = offFilePath[0];
+ if (fs.existsSync(offHelpers.objectPath() + '/' + offFilePath) === false) {
+ writeStream = fs.createWriteStream(offHelpers.objectPath() + '/' + offFilePath);
+ process.stdin.pipe(writeStream);
+ fs.chmodSync(offHelpers.objectPath() + '/' + offFilePath, '444');
+ } else {
+ writeStream = fs.createWriteStream('/dev/null');
+ process.stdin.pipe(writeStream);
+ }
+ console.log(offDEFAULTS.offSignature + offFile + ' size:' + size);
+ },
+ 'prepush': function() {
+ var rl;
+ offHelpers.setTransport();
+ rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+ terminal: false
+ });
+ rl.on('line', function(line) {
+ offCommands.push(line);
+ });
+ },
+ 'push': function(line) {
+ var commitListToPush, i, j, len, len1, len2, line_l, localRef, localSha, objInfo, objInfoList, offFile, offFilePath, offRef, oi, r, remoteName, remoteRef, remoteSha, sha, url;
+ len = process.argv.length;
+ remoteName = process.argv[len - 2];
+ url = process.argv[len - 1];
+ line_l = line.split(' ');
+ localRef = line_l[0];
+ localSha = line_l[1];
+ remoteRef = line_l[2];
+ remoteSha = line_l[3];
+ r = exec('gitList', [localSha, '^' + remoteSha]);
+ commitListToPush = r.stdout.split('\n');
+ commitListToPush = commitListToPush.slice(0, commitListToPush.length - 1);
+ for (i = 0, len1 = commitListToPush.length; i < len1; i++) {
+ sha = commitListToPush[i];
+ r = exec('gitDiff', [sha]);
+ objInfoList = r.stdout.split('\n');
+ objInfoList = objInfoList.slice(1, objInfoList.length - 1);
+ for (j = 0, len2 = objInfoList.length; j < len2; j++) {
+ oi = objInfoList[j];
+ objInfo = oi.split(' ');
+ if (objInfo[oiNAME].slice(0, 1) !== 'D') {
+ r = exec('gitCat', [objInfo[oiOID]]);
+ offRef = r.stdout.split('\n')[0];
+ if (offRef.indexOf(offDEFAULTS.offSignature) !== -1) {
+ offFile = offRef.split(':')[1].split(' ')[0];
+ offFilePath = offHelpers.getOffFilePath(offFile)[0];
+ transport.send(offFilePath);
+ }
+ }
+ }
+ }
+ },
+ 'smudge': function() {
+ var status;
+ offHelpers.setTransport();
+ offCommands.localSetup();
+ status = 'header';
+ process.stdin.on('readable', function() {
+ var data, decoder, header, offFile, offFilePath, readStream;
+ if (status === 'header') {
+ status = 'stream';
+ data = process.stdin.read(offDEFAULTS.offSignature.length + offDEFAULTS.shaLength);
+ decoder = new StringDecoder('utf8');
+ if (data === null) {
+ offLogRepo('smudge error: cant get data from stdin.');
+ process.exit(1);
+ }
+ header = decoder.write(data);
+ if (header.slice(0, offDEFAULTS.offSignature.length) === offDEFAULTS.offSignature) {
+ offFile = header.split(':')[1];
+ offFilePath = offHelpers.getOffFilePath(offFile)[0];
+ if (fs.existsSync(offHelpers.objectPath() + '/' + offFilePath) === true) {
+ readStream = fs.createReadStream(offHelpers.objectPath() + '/' + offFilePath);
+ readStream.pipe(process.stdout);
+ } else {
+ transport.receive(offFilePath);
+ }
+ } else {
+ process.stdout.write(data);
+ process.stdin.pipe(process.stdout);
+ }
+ }
+ });
+ },
+ 'clearAll': function() {
+ if (offHelpers.offMode() === 'copy') {
+ rmAll(offHelpers.offStore());
+ }
+ if (offHelpers.offMode() === 'scp') {
+ offHelpers.rmAllStore('');
+ }
+ rmAll(offHelpers.objectPath());
+ rmAll(offHelpers.getLog());
+ },
+ 'clearCache': function() {
+ rmAll(offHelpers.objectPath());
+ },
+ 'defaults': function() {
+ var i, k, len1, ref;
+ ref = Object.keys(offDEFAULTS);
+ for (i = 0, len1 = ref.length; i < len1; i++) {
+ k = ref[i];
+ console.log(k.blue.bold + ' ' + offDEFAULTS[k]);
+ }
+ },
+ 'env': function() {
+ console.log('off.mode '.blue.bold + offHelpers.offMode());
+ console.log('off.store '.blue.bold + offHelpers.offStore());
+ console.log('off.scphost '.blue.bold + offHelpers.offScp());
+ console.log('off.scpuser '.blue.bold + offHelpers.offScpUser());
+ console.log('off.log '.blue.bold + offHelpers.getLog());
+ }
+};
+
+
+/*
+ * main
+ */
+
+thisrepo = function(cmd) {
+ if (process.argv[3] !== 'thisrepo') {
+ cmd(gitConfig['set']);
+ } else {
+ cmd(gitConfig['setThisRepo']);
+ }
+};
+
+if (process.argv[2] === 'install') {
+ thisrepo(offCommands['install']);
+}
+
+if (process.argv[2] === 'mode') {
+ thisrepo(offCommands['mode']);
+}
+
+if (process.argv[2] === 'scp') {
+ thisrepo(offCommands['scp']);
+}
+
+if (process.argv[2] === 'scpuser') {
+ thisrepo(offCommands['scpUser']);
+}
+
+if (process.argv[2] === 'track') {
+ offCommands.track();
+}
+
+if (process.argv[2] === 'clean') {
+ offCommands.clean();
+}
+
+if (process.argv[2] === 'pre-push') {
+ offCommands.prepush();
+}
+
+if (process.argv[2] === 'smudge') {
+ offCommands.smudge();
+}
+
+if ((process.argv[2] === 'clearAll') || (process.argv[2] === 'ca')) {
+ offCommands.clearAll();
+}
+
+if ((process.argv[2] === 'clearCache') || (process.argv[2] === 'cc')) {
+ offCommands.clearCache();
+}
+
+if (process.argv[2] === 'defaults') {
+ offCommands.defaults();
+}
+
+if (process.argv[2] === 'env') {
+ offCommands.env();
+}
+
+if (process.argv[2] === 'help' || process.argv[2] === void 0) {
+ console.log('git-off help\n'.green.bold);
+ if (process.argv[3] === void 0) {
+ console.log('# USAGE'.green.bold);
+ console.log('Setup:');
+ console.log("git off track '*.bin'");
+ console.log(" git off now handles files with bin extensions".green);
+ console.log('git add .');
+ console.log(' use git normally'.green);
+ console.log(' git off caches bin files'.green);
+ console.log('git commit');
+ console.log(' git off caches new bin files'.green);
+ console.log('git push');
+ console.log(' git off sends bin files to store'.green);
+ console.log('git checkout master');
+ console.log(' checkout commit with bin files'.green);
+ console.log(' git off downloads bin files from store'.green);
+ console.log('\n# Other\n'.green.bold);
+ console.log('git off install');
+ console.log(' setup git config (default global)'.green);
+ console.log('git off mode scp');
+ console.log(' store object in a remote sshhost, setup host and path with git off scp'.green);
+ console.log('git off scp localhost:/tmp/offStore');
+ console.log(' setup host, port and path'.green);
+ console.log(' user@localhost:port/path'.green);
+ console.log('git off scpuser username');
+ console.log(' setup scp username config'.green);
+ console.log('git off cc');
+ console.log(' clear cache in current repo'.green);
+ console.log('git off ca');
+ console.log(' clear store and cache'.green);
+ console.log('git off env');
+ console.log(' shows current config'.green);
+ console.log('git off defaults');
+ console.log(' shows first time config'.green);
+ } else {
+ console.log('Command help TODO');
+ }
+}
diff --git a/src/gitoff.coffee b/src/gitoff.coffee
@@ -146,7 +146,7 @@ externalHelpers =
'sha': 'git hash-object --no-filters'
'listAttr': 'git check-attr -a'
'ssh': 'ssh -C -o StrictHostKeyChecking=no -o ConnectTimeout=3'
- 'scp': 'scp -C -o StrictHostKeyChecking=no -o ConnectTimeout=3'
+ 'scp': 'scp -C -o StrictHostKeyChecking=no -o ConnectTimeout=3 -p'
# config built by offHelpers
# use offHelpers to access runtimeConfig
@@ -275,6 +275,9 @@ copy = (src, dst) ->
# log: sets and returns runtimeConfig.log
# getLog: sets and returns runtimeConfig.log with error checking
# userAt: returns user@ or ''
+# getSSHConfig extracts host, port and path from off.sshhost
+# mkdirStore mkdir in remote store
+# rmAllStore rm in remote store
# setTransport: set send and receive functions for transport
# getOffFilePath: creates path for offFile
offHelpers =
@@ -303,8 +306,7 @@ offHelpers =
'offScp': ->
if runtimeConfig.offScp == ''
- r = gitConfig.get 'off.scphost'
- runtimeConfig.offScp = expandHome r
+ runtimeConfig.offScp = gitConfig.get 'off.scphost'
runtimeConfig.offScp
'offScpUser': ->
@@ -335,9 +337,59 @@ offHelpers =
user = ''
user
+ 'getSSHConfig': ->
+ # extract host, port and path from off.sshhost
+
+ user = offHelpers.userAt()
+
+ h_l = offHelpers.offScp().split(':')
+ portAndPath_l = h_l[1].split('/')
+ port = parseInt portAndPath_l[0]
+ if port == NaN
+ # use default port
+ host = h_l[0]
+ storePath = h_l[1]
+ else
+ host = h_l[0]
+ storePath = '/' + portAndPath_l.slice(1).join('/')
+ [user + host, storePath, port]
+
+
+ 'mkdirStore': (path) ->
+ # mkdir in remote store
+
+ # TODO handle multiple transports
+ h_l = offHelpers.getSSHConfig()
+ # bug in coffeescript - cant have '"mkdir '+ h_l[1] +'"'
+ sshCmd = '"mkdir -p '+ h_l[1] + '/' + path
+ sshCmd += '"'
+
+ # ignore error from mkdir for already existing store
+ if h_l[2] == NaN
+ exec 'ssh', [h_l[0], sshCmd], true
+ else
+ exec 'ssh', ['-p ' + h_l[2], h_l[0], sshCmd], true
+ return
+
+ 'rmAllStore': (path) ->
+ # rm in remote store
+
+ # scphost format is host:path
+ h_l = offHelpers.getSSHConfig()
+ # bug in coffeescript - cant have '"mkdir '+ h_l[1] +'"'
+ sshCmd = '"rm -rf '+ h_l[1] + '/' + path
+ sshCmd += '"'
+ # ignore error from rm
+ if h_l[2] == NaN
+ exec 'ssh', [h_l[0], sshCmd], true
+ else
+ exec 'ssh', ['-p ' + h_l[2], h_l[0], sshCmd], true
+ return
+
'setTransport': ->
# set send and receive functions for transport
# copy, scp
+
if offHelpers.offMode() == 'copy'
transport['send'] = (file) ->
# create file directories in store
@@ -358,16 +410,15 @@ offHelpers =
user = offHelpers.userAt()
# create file directories in store
- h_l = offHelpers.offScp().split(':')
f_l = file.split '/'
- # bug in coffeescript - cant have '"mkdir '+ h_l[1] +'"'
- sshCmd = '"mkdir -p '+ h_l[1] + '/' + f_l[0] + '/' + f_l[1]
- sshCmd += '"'
- # ignore error from mkdir for already existing store
- exec 'ssh', [user + h_l[0], sshCmd], true
-
+ offHelpers.mkdirStore f_l[0] + '/' + f_l[1]
- exec 'scp', [offHelpers.objectPath() + '/' + file, user + offHelpers.offScp() + '/' + file]
+ #TODO remove user
+ h_l = offHelpers.getSSHConfig()
+ if h_l[2] == NaN
+ exec 'scp', [offHelpers.objectPath() + '/' + file, user + offHelpers.offScp() + '/' + file]
+ else
+ exec 'scp', ['-P ' + h_l[2], offHelpers.objectPath() + '/' + file, h_l[0] + ':' + h_l[1] + '/' + file]
return
transport['receive'] = (file) ->
# create file directories in cache
@@ -376,7 +427,14 @@ offHelpers =
mkdirParents offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]
user = offHelpers.userAt()
- exec 'scp', [user + offHelpers.offScp() + '/' + file, offHelpers.objectPath() + '/' + file]
+
+ #TODO remove user
+ h_l = offHelpers.getSSHConfig()
+ if h_l[2] == NaN
+ exec 'scp', [user + offHelpers.offScp() + '/' + file, offHelpers.objectPath() + '/' + file]
+ else
+ exec 'scp', ['-P ' + h_l[2], h_l[0] + ':' + h_l[1] + '/' + file, offHelpers.objectPath() + '/' + file]
+
readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file)
readStream.pipe(process.stdout)
return
@@ -467,14 +525,7 @@ offCommands =
mkdirParents runtimeConfig.offStore
if runtimeConfig.offMode == 'scp'
- user = offHelpers.userAt()
- # scphost format is host:path
- h_l = offHelpers.offScp().split(':')
- # bug in coffeescript - cant have '"mkdir '+ h_l[1] +'"'
- sshCmd = '"mkdir '+ h_l[1]
- sshCmd += '"'
- # ignore error from mkdir for already existing store
- exec 'ssh', [user + h_l[0], sshCmd], true
+ offHelpers.mkdirStore ''
return
'mode': (setCfg) ->
@@ -695,14 +746,7 @@ offCommands =
if offHelpers.offMode() == 'copy'
rmAll offHelpers.offStore()
if offHelpers.offMode() == 'scp'
- user = offHelpers.userAt()
- # scphost format is host:path
- h_l = offHelpers.offScp().split(':')
- # bug in coffeescript - cant have '"mkdir '+ h_l[1] +'"'
- sshCmd = '"rm -rf '+ h_l[1]
- sshCmd += '"'
- # ignore error from rm
- exec 'ssh', [user + h_l[0], sshCmd], true
+ offHelpers.rmAllStore ''
rmAll offHelpers.objectPath()
rmAll offHelpers.getLog()
return
@@ -802,7 +846,8 @@ if process.argv[2] == 'help' or process.argv[2] == undefined
console.log 'git off mode scp'
console.log ' store object in a remote sshhost, setup host and path with git off scp'.green
console.log 'git off scp localhost:/tmp/offStore'
- console.log ' setup host and path'.green
+ console.log ' setup host, port and path'.green
+ console.log ' user@localhost:port/path'.green
console.log 'git off scpuser username'
console.log ' setup scp username config'.green
console.log 'git off cc'