💾 Archived View for gmi.noulin.net › gitRepositories › git-off › file › src › git-off.gmi captured on 2023-01-29 at 11:40:02. Gemini links have been rewritten to link to archived content

View Raw

More Information

-=-=-=-=-=-=-

git-off

Log

Files

Refs

README

git-off (50220B)

     1 #! /usr/bin/env node
     2 /*
     3  * CODE
     4  *
     5  * modules
     6  * defaults
     7  * helpers
     8  * core
     9  * main
    10  *
    11  *
    12  * modules
    13  *   all dependencies
    14  *
    15  * defaults
    16  *   objInfo fields:  for indexing cat-diff response in push command
    17  *   externalHelpers: shell commands to be used with the exec function
    18  *   runtimeConfig:   config built by offHelpers
    19  *   offDEFAULTS:     default configuration for first time install
    20  *
    21  * helpers
    22  *   expandHome:      expands ~/
    23  *   gitConfig:       handles global git config
    24  *   offLog:          appends log to git config off.log
    25  *   offLogRepo:      log for commands run in git repo
    26  *   exec:            runs an externalHelpers with parameters
    27  *   mkdirParents:    recursive mkdir
    28  *   rmAll:           delete recursively files and directories
    29  *   copy:            copies files
    30  *   offHelpers:      git-off helpers
    31  *
    32  * core
    33  *   transport:       transport functions for git off store
    34  *   offCommands:     command line functions
    35  *
    36  * main
    37  *   parse CLI arguments
    38  */
    39 
    40 /*
    41  * modules
    42  */
    43 var COMMAND_MAP, StringDecoder, copy, exec, expandHome, externalHelpers, fs, fssync, gitConfig, mkdirParents, mkdirp, offCommands, offDEFAULTS, offHelpers, offLog, offLogRepo, oiNAME, oiOID, oiPERMISSIONS, oiPREVIOUSOID, oiPREVIOUSPERMISSIONS, path, readline, rimraf, rmAll, runtimeConfig, showCommandHelp, syncexec, thisrepo, transport, walkSync;
    44 
    45 require('colors');
    46 
    47 fs = require('fs');
    48 
    49 path = require('path');
    50 
    51 fssync = require('fs-sync');
    52 
    53 syncexec = require('sync-exec');
    54 
    55 mkdirp = require('mkdirp');
    56 
    57 rimraf = require('rimraf');
    58 
    59 readline = require('readline');
    60 
    61 StringDecoder = require('string_decoder').StringDecoder;
    62 
    63 
    64 /*
    65  * defaults
    66  */
    67 
    68 oiPREVIOUSPERMISSIONS = 0;
    69 
    70 oiPERMISSIONS = 1;
    71 
    72 oiPREVIOUSOID = 2;
    73 
    74 oiOID = 3;
    75 
    76 oiNAME = 4;
    77 
    78 externalHelpers = {
    79   'gitConfigGlobal': 'git config --global',
    80   'gitConfig': 'git config',
    81   'gitRepoRoot': 'git rev-parse --show-toplevel',
    82   'gitList': 'git rev-list',
    83   'gitListEmptyRemote': 'git rev-list --max-parents=0',
    84   'gitDiff': 'git show --raw --format="" --no-abbrev',
    85   'gitCat': 'git cat-file -p',
    86   'sha': 'git hash-object --no-filters',
    87   'listAttr': 'git check-attr -a',
    88   'ssh': 'ssh',
    89   'scp': 'scp',
    90   'curl': 'curl',
    91   'mv': 'mv',
    92   'rsync': 'rsync'
    93 };
    94 
    95 runtimeConfig = {
    96   'currentRepoRoot': '',
    97   'objectPath': '',
    98   'offStore': '',
    99   'offHttp': '',
   100   'offMode': '',
   101   'offIntegrity': '',
   102   'offScp': '',
   103   'offScpUser': '',
   104   'offPem': '',
   105   'offSshOptions': '',
   106   'offScpOptions': '',
   107   'offRsyncOptions': '',
   108   'offCurlOptions': '',
   109   'log': '',
   110   'offConfigAlways': '',
   111   's3Region': '',
   112   's3Bucket': '',
   113   'transform': '',
   114   'transformTo': '',
   115   'transformFrom': ''
   116 };
   117 
   118 offDEFAULTS = {
   119   'objectPath': '/.git/off/objects',
   120   'mode': 'copy',
   121   'integrity': 'disable',
   122   'pem': 'offNoValue',
   123   'scpHost': 'offNoValue',
   124   'scpUser': '',
   125   'sshOptions': '-C -o StrictHostKeyChecking=no -o ConnectTimeout=3',
   126   'scpOptions': '-C -o StrictHostKeyChecking=no -o ConnectTimeout=3 -p',
   127   'rsyncOptions': '-az -e \'ssh -i _i\'',
   128   'store': '~/.git-off/offStore',
   129   'http': 'offNoValue',
   130   'curlOptions': '-o',
   131   'log': '~/.git-off/log',
   132   'configAlways': 'offNoValue',
   133   's3Region': 'offNoValue',
   134   's3Bucket': 'offNoValue',
   135   'transform': 'disable',
   136   'transformTo': 'pbzip2 -9 -c _1 > _2',
   137   'transformFrom': 'pbzip2 -d -c _1 > _2',
   138   '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 "$@"',
   139   'offSignature': '### git-off v1 sha:',
   140   'shaLength': 40
   141 };
   142 
   143 
   144 /*
   145  * helpers
   146  */
   147 
   148 expandHome = function(p) {
   149   if (p.slice(0, 2) === '~/') {
   150     p = process.env.HOME + '/' + p.slice(2);
   151   }
   152   return p;
   153 };
   154 
   155 gitConfig = {
   156   'set': function(key, value) {
   157     exec('gitConfig', ['--global', key, '"' + value + '"']);
   158   },
   159   'setLocal': function(key, value) {
   160     exec('gitConfig', [key, '"' + value + '"']);
   161   },
   162   'setThisRepo': function(key, value) {
   163     exec('gitConfig', ['--file ' + offHelpers.gitRepoRoot() + '/.git-off', key, '"' + value + '"']);
   164   },
   165   'get': function(key) {
   166     var r;
   167     if (offHelpers.offConfigAlways() === 'GIT_OFF_CONFIG') {
   168       r = '';
   169       if (process.env.GIT_OFF_CONFIG !== void 0 && fs.existsSync(process.env.GIT_OFF_CONFIG) === true) {
   170         r = exec('gitConfig', ['--file ' + process.env.GIT_OFF_CONFIG, key]).stdout.trim();
   171       }
   172       return r;
   173     }
   174     if (offHelpers.offConfigAlways() === 'repo') {
   175       r = '';
   176       if (fs.existsSync(offHelpers.gitRepoRoot(true) + '/.git-off') === true) {
   177         r = exec('gitConfig', ['--file ' + offHelpers.gitRepoRoot() + '/.git-off', key]).stdout.trim();
   178       }
   179       return r;
   180     }
   181     if (offHelpers.offConfigAlways() === 'global') {
   182       return exec('gitConfig', [key]).stdout.trim();
   183     }
   184     if (process.env.GIT_OFF_CONFIG !== void 0 && fs.existsSync(process.env.GIT_OFF_CONFIG) === true) {
   185       r = exec('gitConfig', ['--file ' + process.env.GIT_OFF_CONFIG, key]).stdout.trim();
   186       if (r !== '') {
   187         return r;
   188       }
   189     }
   190     if (fs.existsSync(offHelpers.gitRepoRoot(true) + '/.git-off') === true) {
   191       r = exec('gitConfig', ['--file ' + offHelpers.gitRepoRoot() + '/.git-off', key]).stdout.trim();
   192       if (r !== '') {
   193         return r;
   194       }
   195     }
   196     return exec('gitConfig', [key]).stdout.trim();
   197   },
   198   'getSyncexec': function(key) {
   199     return syncexec(externalHelpers.gitConfig + ' ' + key);
   200   }
   201 };
   202 
   203 mkdirParents = function(p) {
   204   p = expandHome(p);
   205   mkdirp.sync(p, function(err) {
   206     console.error(err);
   207   });
   208 };
   209 
   210 rmAll = function(p) {
   211   p = expandHome(p);
   212   rimraf.sync(p);
   213 };
   214 
   215 offLog = function(s) {
   216   var r;
   217   if (runtimeConfig.log === '') {
   218     r = gitConfig.getSyncexec('off.log');
   219     runtimeConfig.log = expandHome(r.stdout.trim());
   220     if (runtimeConfig.log === '') {
   221       console.error('Missing off.log config. Run "git config --global off.log ~/.git-off/log".'.red.bold);
   222       process.exit(1);
   223     }
   224   }
   225   if (fs.existsSync(path.dirname(runtimeConfig.log)) === false) {
   226     mkdirParents(path.dirname(runtimeConfig.log));
   227   }
   228   fs.appendFileSync(runtimeConfig.log, s + '\n');
   229 };
   230 
   231 offLogRepo = function(s) {
   232   offLog('REPO path: ' + offHelpers.gitRepoRoot() + ' ' + s);
   233 };
   234 
   235 exec = function(cmdName, paramsArray, ignoreStderr) {
   236   var r;
   237   if (paramsArray == null) {
   238     paramsArray = [];
   239   }
   240   if (ignoreStderr == null) {
   241     ignoreStderr = false;
   242   }
   243   r = syncexec(externalHelpers[cmdName] + ' ' + paramsArray.join(' '));
   244   if (r.stderr !== '' && ignoreStderr === false) {
   245     console.error(r.stderr.red.bold);
   246     offLog(r.stderr.red.bold);
   247     process.exit(1);
   248   }
   249   return r;
   250 };
   251 
   252 copy = function(src, dst) {
   253   fssync.copy(src, dst);
   254 };
   255 
   256 walkSync = function(dir, filelist) {
   257   var files;
   258   if (filelist == null) {
   259     filelist = [];
   260   }
   261   files = fs.readdirSync(dir);
   262   files.forEach(function(file) {
   263     if (fs.statSync(dir + '/' + file).isDirectory()) {
   264       filelist = walkSync(dir + '/' + file, filelist);
   265     } else {
   266       filelist.push(dir + '/' + file);
   267     }
   268   });
   269   return filelist;
   270 };
   271 
   272 offHelpers = {
   273   'gitRepoRoot': function(ignoreStderr) {
   274     var r;
   275     if (ignoreStderr == null) {
   276       ignoreStderr = false;
   277     }
   278     if (runtimeConfig.currentRepoRoot === '') {
   279       r = exec('gitRepoRoot', [], ignoreStderr);
   280       runtimeConfig.currentRepoRoot = r.stdout.trim();
   281     }
   282     return runtimeConfig.currentRepoRoot;
   283   },
   284   'objectPath': function() {
   285     if (runtimeConfig.objectPath === '') {
   286       runtimeConfig.objectPath = this.gitRepoRoot() + offDEFAULTS.objectPath;
   287     }
   288     return runtimeConfig.objectPath;
   289   },
   290   'offStore': function() {
   291     var r;
   292     if (runtimeConfig.offStore === '') {
   293       r = gitConfig.get('off.store');
   294       runtimeConfig.offStore = expandHome(r);
   295     }
   296     return runtimeConfig.offStore;
   297   },
   298   'offHttp': function() {
   299     if (runtimeConfig.offHttp === '') {
   300       runtimeConfig.offHttp = gitConfig.get('off.http');
   301     }
   302     return runtimeConfig.offHttp;
   303   },
   304   'offCurlOptions': function() {
   305     if (runtimeConfig.offCurlOptions === '') {
   306       runtimeConfig.offCurlOptions = gitConfig.get('off.curloptions');
   307     }
   308     return runtimeConfig.offCurlOptions;
   309   },
   310   'offMode': function() {
   311     if (runtimeConfig.offMode === '') {
   312       runtimeConfig.offMode = gitConfig.get('off.mode');
   313     }
   314     return runtimeConfig.offMode;
   315   },
   316   'offIntegrity': function() {
   317     if (runtimeConfig.offIntegrity === '') {
   318       runtimeConfig.offIntegrity = gitConfig.get('off.integrity');
   319     }
   320     return runtimeConfig.offIntegrity;
   321   },
   322   'offPem': function() {
   323     var r;
   324     if (runtimeConfig.offPem === '') {
   325       r = gitConfig.get('off.pem');
   326       runtimeConfig.offPem = expandHome(r);
   327     }
   328     return runtimeConfig.offPem;
   329   },
   330   'offSshOptions': function() {
   331     if (runtimeConfig.offSshOptions === '') {
   332       runtimeConfig.offSshOptions = gitConfig.get('off.sshoptions');
   333     }
   334     return runtimeConfig.offSshOptions;
   335   },
   336   'offScpOptions': function() {
   337     if (runtimeConfig.offScpOptions === '') {
   338       runtimeConfig.offScpOptions = gitConfig.get('off.scpoptions');
   339     }
   340     return runtimeConfig.offScpOptions;
   341   },
   342   'offRsyncOptions': function() {
   343     if (runtimeConfig.offRsyncOptions === '') {
   344       runtimeConfig.offRsyncOptions = gitConfig.get('off.rsyncoptions');
   345     }
   346     return runtimeConfig.offRsyncOptions;
   347   },
   348   'offScp': function() {
   349     if (runtimeConfig.offScp === '') {
   350       runtimeConfig.offScp = gitConfig.get('off.scphost');
   351     }
   352     return runtimeConfig.offScp;
   353   },
   354   'offScpUser': function() {
   355     if (runtimeConfig.offScpUser === '') {
   356       runtimeConfig.offScpUser = gitConfig.get('off.scpuser');
   357     }
   358     return runtimeConfig.offScpUser;
   359   },
   360   'log': function() {
   361     var r;
   362     if (runtimeConfig.log === '') {
   363       r = gitConfig.getSyncexec('off.log');
   364       runtimeConfig.log = expandHome(r.stdout.trim());
   365     }
   366     return runtimeConfig.log;
   367   },
   368   'getLog': function() {
   369     offHelpers.log();
   370     if (runtimeConfig.log === '') {
   371       console.error('Missing off.log config. Run "git config --global off.log ~/.git-off/log".'.red.bold);
   372       process.exit(1);
   373     }
   374     return runtimeConfig.log;
   375   },
   376   'offConfigAlways': function() {
   377     var r;
   378     if (runtimeConfig.offConfigAlways === '') {
   379       r = gitConfig.getSyncexec('off.configAlways');
   380       runtimeConfig.offConfigAlways = r.stdout.trim();
   381     }
   382     return runtimeConfig.offConfigAlways;
   383   },
   384   's3Region': function() {
   385     if (runtimeConfig.s3Region === '') {
   386       runtimeConfig.s3Region = gitConfig.get('off.s3region');
   387     }
   388     return runtimeConfig.s3Region;
   389   },
   390   's3Bucket': function() {
   391     if (runtimeConfig.s3Bucket === '') {
   392       runtimeConfig.s3Bucket = gitConfig.get('off.s3bucket');
   393     }
   394     return runtimeConfig.s3Bucket;
   395   },
   396   'transform': function() {
   397     if (runtimeConfig.transform === '') {
   398       runtimeConfig.transform = gitConfig.get('off.transform');
   399     }
   400     return runtimeConfig.transform;
   401   },
   402   'transformTo': function() {
   403     if (runtimeConfig.transformTo === '') {
   404       runtimeConfig.transformTo = gitConfig.get('off.transformTo');
   405     }
   406     return runtimeConfig.transformTo;
   407   },
   408   'transformFrom': function() {
   409     if (runtimeConfig.transformFrom === '') {
   410       runtimeConfig.transformFrom = gitConfig.get('off.transformFrom');
   411     }
   412     return runtimeConfig.transformFrom;
   413   },
   414   'userAt': function() {
   415     var user;
   416     if (offHelpers.offScpUser() !== '') {
   417       user = offHelpers.offScpUser() + '@';
   418     } else {
   419       user = '';
   420     }
   421     return user;
   422   },
   423   'getSSHConfig': function() {
   424     var h_l, host, port, portAndPath_l, storePath, user;
   425     user = offHelpers.userAt();
   426     h_l = offHelpers.offScp().split(':');
   427     if (h_l[1] === void 0) {
   428       port = NaN;
   429     } else {
   430       portAndPath_l = h_l[1].split('/');
   431       port = parseInt(portAndPath_l[0]);
   432     }
   433     if (isNaN(port)) {
   434       host = h_l[0];
   435       storePath = h_l[1];
   436     } else {
   437       host = h_l[0];
   438       storePath = '/' + portAndPath_l.slice(1).join('/');
   439     }
   440     return [user + host, storePath, port];
   441   },
   442   'mkdirStore': function(p) {
   443     var h_l, pem, sshCmd;
   444     h_l = offHelpers.getSSHConfig();
   445     sshCmd = '"mkdir -p ' + h_l[1] + '/' + p;
   446     sshCmd += '"';
   447     if (offHelpers.offPem() === '' || offHelpers.offPem() === 'offNoValue') {
   448       pem = '';
   449     } else {
   450       pem = '-i ' + offHelpers.offPem();
   451     }
   452     if (isNaN(h_l[2])) {
   453       exec('ssh', [offHelpers.offSshOptions(), pem, h_l[0], sshCmd], true);
   454     } else {
   455       exec('ssh', [offHelpers.offSshOptions(), pem, '-p ' + h_l[2], h_l[0], sshCmd], true);
   456     }
   457   },
   458   'rmAllStore': function(p) {
   459     var h_l, pem, sshCmd;
   460     if (offHelpers.offPem() === '' || offHelpers.offPem() === 'offNoValue') {
   461       pem = '';
   462     } else {
   463       pem = '-i ' + offHelpers.offPem();
   464     }
   465     h_l = offHelpers.getSSHConfig();
   466     sshCmd = '"rm -rf ' + h_l[1] + '/' + p;
   467     sshCmd += '"';
   468     if (isNaN(h_l[2])) {
   469       exec('ssh', [offHelpers.offSshOptions(), pem, h_l[0], sshCmd], true);
   470     } else {
   471       exec('ssh', [offHelpers.offSshOptions(), pem, '-p ' + h_l[2], h_l[0], sshCmd], true);
   472     }
   473   },
   474   'copyTo': function() {
   475     var f, files, i, j, len1, len2, tfiles;
   476     tfiles = walkSync(offHelpers.objectPath());
   477     files = [];
   478     for (i = 0, len1 = tfiles.length; i < len1; i++) {
   479       f = tfiles[i];
   480       files.push(f.replace(offHelpers.objectPath() + '/', ''));
   481     }
   482     for (j = 0, len2 = files.length; j < len2; j++) {
   483       f = files[j];
   484       transport.send(f);
   485     }
   486   },
   487   'checkIntegrity': function(p) {
   488     var r, receivedSha, result;
   489     result = true;
   490     if (offHelpers.offIntegrity() === 'disable') {
   491       return true;
   492     }
   493     r = exec('sha', [p]);
   494     receivedSha = r.stdout.split(' ')[0].trim();
   495     if (path.basename(p) !== receivedSha) {
   496       console.log('git-off: The file ' + p + ' differs from the one that was pushed.');
   497       result = false;
   498     }
   499     return result;
   500   },
   501   'setTransport': function(mode) {
   502     if (mode == null) {
   503       mode = 'config';
   504     }
   505     if (mode === 'config') {
   506       mode = offHelpers.offMode();
   507     }
   508     if (mode === 'copy') {
   509       transport['send'] = function(file) {
   510         var f_l;
   511         f_l = file.split('/');
   512         if (fs.existsSync(offHelpers.offStore() + '/' + f_l[0] + '/' + f_l[1]) === false) {
   513           mkdirParents(offHelpers.offStore() + '/' + f_l[0] + '/' + f_l[1]);
   514         }
   515         copy(offHelpers.objectPath() + '/' + file, offHelpers.offStore() + '/' + file);
   516         fs.chmodSync(offHelpers.offStore() + '/' + file, '444');
   517       };
   518       transport['receive'] = function(file) {
   519         var f_l, readStream;
   520         if (offHelpers.transform() === 'enable' && offHelpers.transformFrom() !== '') {
   521           f_l = file.split('/');
   522           if (fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) === false) {
   523             mkdirParents(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]);
   524           }
   525           copy(offHelpers.offStore() + '/' + file, offHelpers.objectPath() + '/' + file);
   526           transport['transformFrom'](file);
   527           if (offHelpers.checkIntegrity(offHelpers.objectPath() + '/../tmp/' + file)) {
   528             readStream = fs.createReadStream(offHelpers.objectPath() + '/../tmp/' + file);
   529             readStream.pipe(process.stdout);
   530           }
   531         } else {
   532           if (offHelpers.checkIntegrity(offHelpers.offStore() + '/' + file)) {
   533             readStream = fs.createReadStream(offHelpers.offStore() + '/' + file);
   534             readStream.pipe(process.stdout);
   535           }
   536         }
   537       };
   538     } else if (mode === 'rsync') {
   539       transport['send'] = function(file) {
   540         var f_l, options;
   541         f_l = file.split('/');
   542         offHelpers.mkdirStore(f_l[0] + '/' + f_l[1]);
   543         options = offHelpers.offRsyncOptions();
   544         if (options.indexOf('_i') !== -1) {
   545           if (offHelpers.offPem() !== '' && offHelpers.offPem() !== 'offNoValue') {
   546             options = options.replace('_i', offHelpers.offPem());
   547           }
   548         }
   549         exec('rsync', [options, offHelpers.objectPath() + '/' + file, offHelpers.offScp() + '/' + file]);
   550       };
   551       transport['receive'] = function(file) {
   552         var f_l, options, readStream;
   553         f_l = file.split('/');
   554         if (fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) === false) {
   555           mkdirParents(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]);
   556         }
   557         options = offHelpers.offRsyncOptions();
   558         if (options.indexOf('_i') !== -1) {
   559           if (offHelpers.offPem() !== '' && offHelpers.offPem() !== 'offNoValue') {
   560             options = options.replace('_i', offHelpers.offPem());
   561           }
   562         }
   563         exec('rsync', [options, offHelpers.offScp() + '/' + file, offHelpers.objectPath() + '/' + file]);
   564         if (offHelpers.transform() === 'enable' && offHelpers.transformFrom() !== '') {
   565           transport['transformFrom'](file);
   566           if (offHelpers.checkIntegrity(offHelpers.objectPath() + '/../tmp/' + file)) {
   567             readStream = fs.createReadStream(offHelpers.objectPath() + '/../tmp/' + file);
   568             readStream.pipe(process.stdout);
   569           }
   570         } else {
   571           if (offHelpers.checkIntegrity(offHelpers.objectPath() + '/' + file)) {
   572             readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file);
   573             readStream.pipe(process.stdout);
   574           }
   575         }
   576       };
   577     } else if (mode === 'scp') {
   578       transport['send'] = function(file) {
   579         var f_l, h_l, pem;
   580         f_l = file.split('/');
   581         offHelpers.mkdirStore(f_l[0] + '/' + f_l[1]);
   582         if (offHelpers.offPem() === '' || offHelpers.offPem() === 'offNoValue') {
   583           pem = '';
   584         } else {
   585           pem = '-i ' + offHelpers.offPem();
   586         }
   587         h_l = offHelpers.getSSHConfig();
   588         if (isNaN(h_l[2])) {
   589           exec('scp', [offHelpers.offScpOptions(), pem, offHelpers.objectPath() + '/' + file, h_l[0] + ':' + h_l[1] + '/' + file]);
   590         } else {
   591           exec('scp', [offHelpers.offScpOptions(), pem, '-P ' + h_l[2], offHelpers.objectPath() + '/' + file, h_l[0] + ':' + h_l[1] + '/' + file]);
   592         }
   593       };
   594       transport['receive'] = function(file) {
   595         var f_l, h_l, pem, readStream;
   596         f_l = file.split('/');
   597         if (fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) === false) {
   598           mkdirParents(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]);
   599         }
   600         if (offHelpers.offPem() === '' || offHelpers.offPem() === 'offNoValue') {
   601           pem = '';
   602         } else {
   603           pem = '-i ' + offHelpers.offPem();
   604         }
   605         h_l = offHelpers.getSSHConfig();
   606         if (isNaN(h_l[2])) {
   607           exec('scp', [offHelpers.offScpOptions(), pem, h_l[0] + ':' + h_l[1] + '/' + file, offHelpers.objectPath() + '/' + file]);
   608         } else {
   609           exec('scp', [offHelpers.offScpOptions(), pem, '-P ' + h_l[2], h_l[0] + ':' + h_l[1] + '/' + file, offHelpers.objectPath() + '/' + file]);
   610         }
   611         if (offHelpers.transform() === 'enable' && offHelpers.transformFrom() !== '') {
   612           transport['transformFrom'](file);
   613           if (offHelpers.checkIntegrity(offHelpers.objectPath() + '/../tmp/' + file)) {
   614             readStream = fs.createReadStream(offHelpers.objectPath() + '/../tmp/' + file);
   615             readStream.pipe(process.stdout);
   616           }
   617         } else {
   618           if (offHelpers.checkIntegrity(offHelpers.objectPath() + '/' + file)) {
   619             readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file);
   620             readStream.pipe(process.stdout);
   621           }
   622         }
   623       };
   624     } else if (mode === 'http') {
   625       transport['send'] = function(file) {
   626         offLogRepo('Http mode is read-only: ' + file + ' is stored in cache only');
   627       };
   628       transport['receive'] = function(file) {
   629         var f_l, readStream;
   630         f_l = file.split('/');
   631         if (fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) === false) {
   632           mkdirParents(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]);
   633         }
   634         exec('curl', [offHelpers.offCurlOptions(), offHelpers.objectPath() + '/' + file, offHelpers.offHttp() + '/' + file]);
   635         if (offHelpers.transform() === 'enable' && offHelpers.transformFrom() !== '') {
   636           transport['transformFrom'](file);
   637           if (offHelpers.checkIntegrity(offHelpers.objectPath() + '/../tmp/' + file)) {
   638             readStream = fs.createReadStream(offHelpers.objectPath() + '/../tmp/' + file);
   639             readStream.pipe(process.stdout);
   640           }
   641         } else {
   642           if (offHelpers.checkIntegrity(offHelpers.objectPath() + '/' + file)) {
   643             readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file);
   644             readStream.pipe(process.stdout);
   645           }
   646         }
   647       };
   648     } else if (mode === 's3') {
   649       transport['send'] = function(file) {
   650         var AWS, fileStream, s3, upParams;
   651         AWS = require('aws-sdk');
   652         s3 = new AWS.S3({
   653           region: offHelpers.s3Region(),
   654           signatureVersion: 'v4'
   655         });
   656         upParams = {
   657           Bucket: offHelpers.s3Bucket(),
   658           Key: file,
   659           Body: ''
   660         };
   661         fileStream = fs.createReadStream(offHelpers.objectPath() + '/' + file);
   662         upParams.Body = fileStream;
   663         s3.upload(upParams, function(err, data) {});
   664       };
   665       transport['receive'] = function(file) {
   666         var AWS, dlParams, f_l, fileStream, s3;
   667         AWS = require('aws-sdk');
   668         s3 = new AWS.S3({
   669           region: offHelpers.s3Region(),
   670           signatureVersion: 'v4'
   671         });
   672         f_l = file.split('/');
   673         if (fs.existsSync(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]) === false) {
   674           mkdirParents(offHelpers.objectPath() + '/' + f_l[0] + '/' + f_l[1]);
   675         }
   676         dlParams = {
   677           Bucket: offHelpers.s3Bucket(),
   678           Key: file
   679         };
   680         fileStream = fs.createWriteStream(offHelpers.objectPath() + '/' + file);
   681         fileStream.on('finish', function() {
   682           var readStream;
   683           if (offHelpers.transform() === 'enable' && offHelpers.transformFrom() !== '') {
   684             transport['transformFrom'](file);
   685             if (offHelpers.checkIntegrity(offHelpers.objectPath() + '/../tmp/' + file)) {
   686               readStream = fs.createReadStream(offHelpers.objectPath() + '/../tmp/' + file);
   687               readStream.pipe(process.stdout);
   688             }
   689           } else {
   690             if (offHelpers.checkIntegrity(offHelpers.objectPath() + '/' + file)) {
   691               readStream = fs.createReadStream(offHelpers.objectPath() + '/' + file);
   692               readStream.pipe(process.stdout);
   693             }
   694           }
   695         });
   696         s3.getObject(dlParams, function(err, data) {}).on('httpData', function(chunk) {
   697           fileStream.write(chunk);
   698         }).on('httpDone', function() {
   699           fileStream.end();
   700         });
   701       };
   702     }
   703   },
   704   'getOffFilePath': function(offFile) {
   705     return [offFile.slice(0, 2) + '/' + offFile.slice(2, 4) + '/' + offFile, offFile.slice(0, 2) + '/' + offFile.slice(2, 4)];
   706   }
   707 };
   708 
   709 
   710 /*
   711  * core
   712  */
   713 
   714 transport = {
   715   'send': function(src) {},
   716   'receive': function(src) {},
   717   'transformFrom': function(file) {
   718     var cmd, offFile, offFilePath, r;
   719     offFile = path.basename(file);
   720     offFilePath = offHelpers.getOffFilePath(offFile);
   721     if (fs.existsSync(offHelpers.objectPath() + '/../tmp/' + offFilePath[1]) === false) {
   722       mkdirParents(offHelpers.objectPath() + '/../tmp/' + offFilePath[1]);
   723     }
   724     cmd = offHelpers.transformFrom();
   725     cmd = cmd.replace('_1', offHelpers.objectPath() + '/' + file).replace('_2', offHelpers.objectPath() + '/../tmp/' + file);
   726     r = syncexec(cmd);
   727   }
   728 };
   729 
   730 offCommands = {
   731   'localSetup': function() {
   732     var hook;
   733     mkdirParents(offHelpers.objectPath());
   734     hook = offHelpers.gitRepoRoot() + '/.git/hooks/pre-push';
   735     if (fs.existsSync(hook) === false) {
   736       fs.writeFileSync(hook, offDEFAULTS.prePush);
   737       fs.chmodSync(hook, '755');
   738     }
   739   },
   740   'install': function(setCfg) {
   741     if (setCfg == null) {
   742       setCfg = gitConfig.set;
   743     }
   744     offCommands.localSetup();
   745     if (gitConfig.get('filter.off.clean') === '' || setCfg !== gitConfig.set) {
   746       if (setCfg === gitConfig.setThisRepo) {
   747         gitConfig.setLocal('filter.off.clean', 'git off clean %f');
   748       } else {
   749         setCfg('filter.off.clean', 'git off clean %f');
   750       }
   751     }
   752     if (gitConfig.get('filter.off.smudge') === '' || setCfg !== gitConfig.set) {
   753       if (setCfg === gitConfig.setThisRepo) {
   754         gitConfig.setLocal('filter.off.smudge', 'git off smudge %f');
   755       } else {
   756         setCfg('filter.off.smudge', 'git off smudge %f');
   757       }
   758     }
   759     if (offHelpers.log() === '' || setCfg !== gitConfig.set) {
   760       gitConfig.set('off.log', offDEFAULTS.log);
   761     }
   762     if (offHelpers.offMode() === '' || setCfg !== gitConfig.set) {
   763       setCfg('off.mode', offDEFAULTS.mode);
   764     }
   765     if (offHelpers.offIntegrity() === '' || setCfg !== gitConfig.set) {
   766       setCfg('off.integrity', offDEFAULTS.integrity);
   767     }
   768     if (offHelpers.offPem() === '' || setCfg !== gitConfig.set) {
   769       setCfg('off.pem', offDEFAULTS.pem);
   770     }
   771     if (offHelpers.offScp() === '' || setCfg !== gitConfig.set) {
   772       setCfg('off.scphost', offDEFAULTS.scpHost);
   773     }
   774     if (offHelpers.offScpUser() === '' || setCfg !== gitConfig.set) {
   775       setCfg('off.scpuser', offDEFAULTS.scpUser);
   776     }
   777     if (offHelpers.offSshOptions() === '' || setCfg !== gitConfig.set) {
   778       setCfg('off.sshoptions', offDEFAULTS.sshOptions);
   779     }
   780     if (offHelpers.offScpOptions() === '' || setCfg !== gitConfig.set) {
   781       setCfg('off.scpoptions', offDEFAULTS.scpOptions);
   782     }
   783     if (offHelpers.offRsyncOptions() === '' || setCfg !== gitConfig.set) {
   784       setCfg('off.rsyncoptions', offDEFAULTS.rsyncOptions);
   785     }
   786     if (offHelpers.offStore() === '' || setCfg !== gitConfig.set) {
   787       setCfg('off.store', offDEFAULTS.store);
   788     }
   789     if (offHelpers.offHttp() === '' || setCfg !== gitConfig.set) {
   790       setCfg('off.http', offDEFAULTS.http);
   791     }
   792     if (offHelpers.offCurlOptions() === '' || setCfg !== gitConfig.set) {
   793       setCfg('off.curloptions', offDEFAULTS.curlOptions);
   794     }
   795     if (offHelpers.offConfigAlways() === '' || setCfg !== gitConfig.set) {
   796       setCfg('off.configAlways', offDEFAULTS.configAlways);
   797     }
   798     if (offHelpers.s3Region() === '' || setCfg !== gitConfig.set) {
   799       setCfg('off.s3Region', offDEFAULTS.s3Region);
   800     }
   801     if (offHelpers.s3Bucket() === '' || setCfg !== gitConfig.set) {
   802       setCfg('off.s3Bucket', offDEFAULTS.s3Bucket);
   803     }
   804     if (offHelpers.transform() === '' || setCfg !== gitConfig.set) {
   805       setCfg('off.transform', offDEFAULTS.transform);
   806     }
   807     if (offHelpers.transformTo() === '' || setCfg !== gitConfig.set) {
   808       setCfg('off.transformTo', offDEFAULTS.transformTo);
   809     }
   810     if (offHelpers.transformFrom() === '' || setCfg !== gitConfig.set) {
   811       setCfg('off.transformFrom', offDEFAULTS.transformFrom);
   812     }
   813     if (runtimeConfig.offMode === 'copy') {
   814       mkdirParents(runtimeConfig.offStore);
   815     }
   816     if (runtimeConfig.offMode === 'scp' || runtimeConfig.offMode === 'rsync') {
   817       offHelpers.mkdirStore('');
   818     }
   819   },
   820   'mode': function(setCfg) {
   821     var len, mode;
   822     len = process.argv.length;
   823     mode = process.argv[len - 1];
   824     if (mode !== 'mode' && mode !== 'thisrepo') {
   825       setCfg('off.mode', mode);
   826     } else {
   827       console.log('off.mode '.blue.bold + offHelpers.offMode());
   828     }
   829   },
   830   'store': function(setCfg) {
   831     var len, store;
   832     len = process.argv.length;
   833     store = process.argv[len - 1];
   834     if (store !== 'store' && store !== 'thisrepo') {
   835       setCfg('off.store', store);
   836     } else {
   837       console.log('off.store '.blue.bold + offHelpers.offStore());
   838     }
   839   },
   840   'scp': function(setCfg) {
   841     var len, scpHost;
   842     len = process.argv.length;
   843     scpHost = process.argv[len - 1];
   844     if (scpHost !== 'scp' && scpHost !== 'thisrepo') {
   845       setCfg('off.scphost', scpHost);
   846     } else {
   847       console.log('off.scp '.blue.bold + offHelpers.offScp());
   848     }
   849   },
   850   'http': function(setCfg) {
   851     var http, len;
   852     len = process.argv.length;
   853     http = process.argv[len - 1];
   854     if (http !== 'http' && http !== 'thisrepo') {
   855       setCfg('off.http', http);
   856     } else {
   857       console.log('off.http '.blue.bold + offHelpers.offHttp());
   858     }
   859   },
   860   'curl': function(setCfg) {
   861     var curl, len;
   862     len = process.argv.length;
   863     curl = process.argv[len - 1];
   864     if (curl !== 'curl' && curl !== 'thisrepo') {
   865       setCfg('off.curloptions', curl);
   866     } else {
   867       console.log('off.curloptions '.blue.bold + offHelpers.offCurlOptions());
   868     }
   869   },
   870   'integrity': function(setCfg) {
   871     var integrity, len;
   872     len = process.argv.length;
   873     integrity = process.argv[len - 1];
   874     if (integrity !== 'integrity' && integrity !== 'thisrepo') {
   875       setCfg('off.integrity', integrity);
   876     } else {
   877       console.log('off.integrity '.blue.bold + offHelpers.offIntegrity());
   878     }
   879   },
   880   'pem': function(setCfg) {
   881     var len, pem;
   882     len = process.argv.length;
   883     pem = process.argv[len - 1];
   884     if (pem !== 'pem' && pem !== 'thisrepo') {
   885       setCfg('off.pem', pem);
   886     } else {
   887       console.log('off.pem '.blue.bold + offHelpers.offPem());
   888     }
   889   },
   890   'sshoptions': function(setCfg) {
   891     var len, sshoptions;
   892     len = process.argv.length;
   893     sshoptions = process.argv[len - 1];
   894     if (sshoptions !== 'sshoptions' && sshoptions !== 'thisrepo') {
   895       setCfg('off.sshoptions', sshoptions);
   896     } else {
   897       console.log('off.sshoptions '.blue.bold + offHelpers.offSshOptions());
   898     }
   899   },
   900   'scpoptions': function(setCfg) {
   901     var len, scpoptions;
   902     len = process.argv.length;
   903     scpoptions = process.argv[len - 1];
   904     if (scpoptions !== 'scpoptions' && scpoptions !== 'thisrepo') {
   905       setCfg('off.scpoptions', scpoptions);
   906     } else {
   907       console.log('off.scpoptions '.blue.bold + offHelpers.offScpOptions());
   908     }
   909   },
   910   'rsyncoptions': function(setCfg) {
   911     var len, rsyncoptions;
   912     len = process.argv.length;
   913     rsyncoptions = process.argv[len - 1];
   914     if (rsyncoptions !== 'rsyncoptions' && rsyncoptions !== 'thisrepo') {
   915       setCfg('off.rsyncoptions', rsyncoptions);
   916     } else {
   917       console.log('off.rsyncoptions '.blue.bold + offHelpers.offRsyncOptions());
   918     }
   919   },
   920   'scpUser': function(setCfg) {
   921     var len, scpUser;
   922     len = process.argv.length;
   923     scpUser = process.argv[len - 1];
   924     if (scpUser !== 'scpuser' && scpUser !== 'thisrepo') {
   925       setCfg('off.scpuser', scpUser);
   926     } else {
   927       console.log('off.scpuser '.blue.bold + offHelpers.offScpUser());
   928     }
   929   },
   930   'track': function() {
   931     var i, l, len1, r, ref;
   932     if (process.argv[3] !== void 0) {
   933       offCommands.install();
   934       fs.appendFileSync(offHelpers.gitRepoRoot() + '/.gitattributes', process.argv[3] + ' filter=off -text\n');
   935     } else {
   936       r = exec('listAttr', ['`cd ' + offHelpers.gitRepoRoot() + '; git ls-files`']);
   937       ref = r.stdout.split('\n');
   938       for (i = 0, len1 = ref.length; i < len1; i++) {
   939         l = ref[i];
   940         if (l.indexOf(': filter: off') !== -1) {
   941           console.log(l);
   942         }
   943       }
   944     }
   945   },
   946   'configAlways': function() {
   947     var configAlways, len;
   948     len = process.argv.length;
   949     configAlways = process.argv[len - 1];
   950     if (configAlways !== 'configAlways') {
   951       gitConfig.set('off.configAlways', configAlways);
   952     } else {
   953       console.log('off.configAlways '.blue.bold + offHelpers.offConfigAlways());
   954     }
   955   },
   956   's3region': function(setCfg) {
   957     var len, region;
   958     len = process.argv.length;
   959     region = process.argv[len - 1];
   960     if (region !== 's3region' && region !== 'thisrepo') {
   961       setCfg('off.s3region', region);
   962     } else {
   963       console.log('off.s3region '.blue.bold + offHelpers.s3Region());
   964     }
   965   },
   966   's3bucket': function(setCfg) {
   967     var bucket, len;
   968     len = process.argv.length;
   969     bucket = process.argv[len - 1];
   970     if (bucket !== 's3bucket' && bucket !== 'thisrepo') {
   971       setCfg('off.s3bucket', bucket);
   972     } else {
   973       console.log('off.s3bucket '.blue.bold + offHelpers.s3Bucket());
   974     }
   975   },
   976   'transform': function(setCfg) {
   977     var len, setting;
   978     len = process.argv.length;
   979     setting = process.argv[len - 1];
   980     if (setting !== 'transform' && setting !== 'thisrepo') {
   981       setCfg('off.transform', setting);
   982     } else {
   983       console.log('off.transform '.blue.bold + offHelpers.transform());
   984     }
   985   },
   986   'transformTo': function(setCfg) {
   987     var len, setting;
   988     len = process.argv.length;
   989     setting = process.argv[len - 1];
   990     if (setting !== 'transformTo' && setting !== 'thisrepo') {
   991       setCfg('off.transformTo', setting);
   992     } else {
   993       console.log('off.transformTo '.blue.bold + offHelpers.transformTo());
   994     }
   995   },
   996   'transformFrom': function(setCfg) {
   997     var len, setting;
   998     len = process.argv.length;
   999     setting = process.argv[len - 1];
  1000     if (setting !== 'transformFrom' && setting !== 'thisrepo') {
  1001       setCfg('off.transformFrom', setting);
  1002     } else {
  1003       console.log('off.transformFrom '.blue.bold + offHelpers.transformFrom());
  1004     }
  1005   },
  1006   'clean': function() {
  1007     var file, offFile, offFilePath, pipe, quotedFile, r, size, writeStream;
  1008     offCommands.localSetup();
  1009     file = process.argv[3];
  1010     size = fs.statSync(file).size;
  1011     quotedFile = '"' + file + '"';
  1012     r = exec('sha', [quotedFile]);
  1013     offFile = r.stdout.split(' ')[0].trim();
  1014     offFilePath = offHelpers.getOffFilePath(offFile);
  1015     if (fs.existsSync(offHelpers.objectPath() + '/' + offFilePath[1]) === false) {
  1016       mkdirParents(offHelpers.objectPath() + '/' + offFilePath[1]);
  1017     }
  1018     offFilePath = offFilePath[0];
  1019     if (fs.existsSync(offHelpers.objectPath() + '/' + offFilePath) === false) {
  1020       writeStream = fs.createWriteStream(offHelpers.objectPath() + '/' + offFilePath);
  1021       pipe = process.stdin.pipe(writeStream);
  1022       pipe.on('finish', function() {
  1023         var cmd;
  1024         fs.chmodSync(offHelpers.objectPath() + '/' + offFilePath, '444');
  1025         if (offHelpers.transform() === 'enable' && offHelpers.transformTo() !== '') {
  1026           if (fs.existsSync(offHelpers.objectPath() + '/../tmp' === false)) {
  1027             mkdirParents(offHelpers.objectPath() + '/../tmp');
  1028           }
  1029           offFilePath = offHelpers.getOffFilePath(offFile);
  1030           if (fs.existsSync(offHelpers.objectPath() + '/../tmp/' + offFilePath[1]) === false) {
  1031             mkdirParents(offHelpers.objectPath() + '/../tmp/' + offFilePath[1]);
  1032           }
  1033           offFilePath = offFilePath[0];
  1034           exec('mv', [offHelpers.objectPath() + '/' + offFilePath, offHelpers.objectPath() + '/../tmp/' + offFilePath]);
  1035           cmd = offHelpers.transformTo();
  1036           cmd = cmd.replace('_1', offHelpers.objectPath() + '/../tmp/' + offFilePath).replace('_2', offHelpers.objectPath() + '/' + offFilePath);
  1037           r = syncexec(cmd);
  1038         }
  1039       });
  1040     } else {
  1041       writeStream = fs.createWriteStream('/dev/null');
  1042       process.stdin.pipe(writeStream);
  1043     }
  1044     console.log(offDEFAULTS.offSignature + offFile + ' size:' + size);
  1045   },
  1046   'prepush': function() {
  1047     var rl;
  1048     offHelpers.setTransport();
  1049     rl = readline.createInterface({
  1050       input: process.stdin,
  1051       output: process.stdout,
  1052       terminal: false
  1053     });
  1054     rl.on('line', function(line) {
  1055       offCommands.push(line);
  1056     });
  1057   },
  1058   'push': function(line) {
  1059     var commitListToPush, i, j, len, len1, len2, line_l, localRef, localSha, objInfo, objInfoList, offFile, offFilePath, offRef, oi, r, remoteName, remoteRef, remoteSha, sha, url;
  1060     len = process.argv.length;
  1061     remoteName = process.argv[len - 2];
  1062     url = process.argv[len - 1];
  1063     line_l = line.split(' ');
  1064     localRef = line_l[0];
  1065     localSha = line_l[1];
  1066     remoteRef = line_l[2];
  1067     remoteSha = line_l[3];
  1068     r = 0;
  1069     if (remoteSha === '0000000000000000000000000000000000000000') {
  1070       r = exec('gitListEmptyRemote', [localSha]);
  1071     } else {
  1072       r = exec('gitList', [localSha, '^' + remoteSha]);
  1073     }
  1074     commitListToPush = r.stdout.split('\n');
  1075     commitListToPush = commitListToPush.slice(0, commitListToPush.length - 1);
  1076     for (i = 0, len1 = commitListToPush.length; i < len1; i++) {
  1077       sha = commitListToPush[i];
  1078       r = exec('gitDiff', [sha]);
  1079       objInfoList = r.stdout.split('\n');
  1080       objInfoList = objInfoList.slice(1, objInfoList.length - 1);
  1081       for (j = 0, len2 = objInfoList.length; j < len2; j++) {
  1082         oi = objInfoList[j];
  1083         objInfo = oi.split(' ');
  1084         if (objInfo[oiNAME].slice(0, 1) !== 'D') {
  1085           r = exec('gitCat', [objInfo[oiOID]]);
  1086           offRef = r.stdout.split('\n')[0];
  1087           if (offRef.indexOf(offDEFAULTS.offSignature) !== -1) {
  1088             offFile = offRef.split(':')[1].split(' ')[0];
  1089             offFilePath = offHelpers.getOffFilePath(offFile)[0];
  1090             transport.send(offFilePath);
  1091           }
  1092         }
  1093       }
  1094     }
  1095   },
  1096   'smudge': function() {
  1097     var status;
  1098     offHelpers.setTransport();
  1099     offCommands.localSetup();
  1100     status = 'header';
  1101     process.stdin.on('readable', function() {
  1102       var cmd, data, decoder, header, offFile, offFilePath, r, readStream;
  1103       if (status === 'header') {
  1104         status = 'stream';
  1105         data = process.stdin.read(offDEFAULTS.offSignature.length + offDEFAULTS.shaLength);
  1106         decoder = new StringDecoder('utf8');
  1107         if (data === null) {
  1108           offLogRepo('smudge error: cant get data from stdin.');
  1109           process.exit(1);
  1110         }
  1111         header = decoder.write(data);
  1112         if (header.slice(0, offDEFAULTS.offSignature.length) === offDEFAULTS.offSignature) {
  1113           offFile = header.split(':')[1];
  1114           offFilePath = offHelpers.getOffFilePath(offFile)[0];
  1115           if (fs.existsSync(offHelpers.objectPath() + '/' + offFilePath) === true) {
  1116             if (offHelpers.transform() === 'enable' && offHelpers.transformFrom() !== '') {
  1117               if (fs.existsSync(offHelpers.objectPath() + '/../tmp' === false)) {
  1118                 mkdirParents(offHelpers.objectPath() + '/../tmp');
  1119               }
  1120               offFilePath = offHelpers.getOffFilePath(offFile);
  1121               if (fs.existsSync(offHelpers.objectPath() + '/../tmp/' + offFilePath[1]) === false) {
  1122                 mkdirParents(offHelpers.objectPath() + '/../tmp/' + offFilePath[1]);
  1123               }
  1124               offFilePath = offFilePath[0];
  1125               cmd = offHelpers.transformFrom();
  1126               cmd = cmd.replace('_1', offHelpers.objectPath() + '/' + offFilePath).replace('_2', offHelpers.objectPath() + '/../tmp/' + offFilePath);
  1127               r = syncexec(cmd);
  1128               readStream = fs.createReadStream(offHelpers.objectPath() + '/../tmp/' + offFilePath);
  1129             } else {
  1130               readStream = fs.createReadStream(offHelpers.objectPath() + '/' + offFilePath);
  1131             }
  1132             readStream.pipe(process.stdout);
  1133           } else {
  1134             transport.receive(offFilePath);
  1135           }
  1136         } else {
  1137           process.stdout.write(data);
  1138           process.stdin.pipe(process.stdout);
  1139         }
  1140       }
  1141     });
  1142   },
  1143   'copyTo': function() {
  1144     if (process.argv[3] === void 0) {
  1145       console.log('Choose a mode where to copy the cache'.red.bold);
  1146       return;
  1147     }
  1148     offHelpers.setTransport(process.argv[3]);
  1149     offHelpers.copyTo();
  1150   },
  1151   'pushTo': function() {
  1152     if (offHelpers.offMode() !== void 0) {
  1153       offHelpers.setTransport(offHelpers.offMode());
  1154       offHelpers.copyTo();
  1155     }
  1156   },
  1157   'clearAll': function() {
  1158     offCommands.clearStore();
  1159     offCommands.clearCache();
  1160     rmAll(offHelpers.getLog());
  1161   },
  1162   'clearCache': function() {
  1163     rmAll(offHelpers.gitRepoRoot() + '/.git/off');
  1164   },
  1165   'clearStore': function() {
  1166     if (offHelpers.offMode() === 'copy') {
  1167       rmAll(offHelpers.offStore());
  1168     }
  1169     if (offHelpers.offMode() === 'scp' || offHelpers.offMode() === 'rsync') {
  1170       offHelpers.rmAllStore('');
  1171     }
  1172   },
  1173   'clearTmp': function() {
  1174     rmAll(offHelpers.gitRepoRoot() + '/.git/off/tmp');
  1175   },
  1176   'defaults': function() {
  1177     var i, k, len1, ref;
  1178     ref = Object.keys(offDEFAULTS);
  1179     for (i = 0, len1 = ref.length; i < len1; i++) {
  1180       k = ref[i];
  1181       console.log(k.blue.bold + ' ' + offDEFAULTS[k]);
  1182     }
  1183   },
  1184   'env': function() {
  1185     console.log('off.mode '.blue.bold + offHelpers.offMode());
  1186     console.log('off.integrity '.blue.bold + offHelpers.offIntegrity());
  1187     console.log('off.pem '.blue.bold + offHelpers.offPem());
  1188     console.log('off.sshoptions '.blue.bold + offHelpers.offSshOptions());
  1189     console.log('off.scpoptions '.blue.bold + offHelpers.offScpOptions());
  1190     console.log('off.rsyncoptions '.blue.bold + offHelpers.offRsyncOptions());
  1191     console.log('off.store '.blue.bold + offHelpers.offStore());
  1192     console.log('off.http '.blue.bold + offHelpers.offHttp());
  1193     console.log('off.curloptions '.blue.bold + offHelpers.offCurlOptions());
  1194     console.log('off.scphost '.blue.bold + offHelpers.offScp());
  1195     console.log('off.scpuser '.blue.bold + offHelpers.offScpUser());
  1196     console.log('off.log '.blue.bold + offHelpers.getLog());
  1197     console.log('off.configAlways '.blue.bold + offHelpers.offConfigAlways());
  1198     console.log('off.s3region '.blue.bold + offHelpers.s3Region());
  1199     console.log('off.s3bucket '.blue.bold + offHelpers.s3Bucket());
  1200     console.log('off.transform '.blue.bold + offHelpers.transform());
  1201     console.log('off.transformTo '.blue.bold + offHelpers.transformTo());
  1202     console.log('off.transformFrom '.blue.bold + offHelpers.transformFrom());
  1203   },
  1204   'help': function() {
  1205     var c, i, len1, ref;
  1206     console.log('git-off help\n'.green.bold);
  1207     if (process.argv[3] === void 0) {
  1208       console.log('# Quick Start'.green.bold);
  1209       console.log('Setup:');
  1210       console.log("git off track '*.bin'");
  1211       console.log('git add .');
  1212       console.log('git commit');
  1213       console.log('git push');
  1214       console.log('git checkout master');
  1215       console.log('\n# Other'.green.bold);
  1216       console.log('git off install');
  1217       console.log('git off mode scp');
  1218       console.log('git off scp localhost:/tmp/offStore');
  1219       console.log('git off scpuser username');
  1220       console.log('git off cc');
  1221       console.log('git off ca');
  1222       console.log('git off env');
  1223       console.log('git off defaults\n');
  1224       ref = Object.keys(COMMAND_MAP);
  1225       for (i = 0, len1 = ref.length; i < len1; i++) {
  1226         c = ref[i];
  1227         showCommandHelp(c);
  1228       }
  1229       console.log('\nGo to https://noulin.net/git-off/file/README.md.html for more information'.green.bold);
  1230     } else {
  1231       if (COMMAND_MAP[process.argv[3]] === void 0) {
  1232         console.log(COMMAND_MAP.help.h.green);
  1233       } else {
  1234         showCommandHelp(process.argv[3]);
  1235       }
  1236     }
  1237   }
  1238 };
  1239 
  1240 
  1241 /*
  1242  * main
  1243  */
  1244 
  1245 thisrepo = function(cmd) {
  1246   if (process.argv[3] !== 'thisrepo') {
  1247     cmd(gitConfig['set']);
  1248   } else {
  1249     cmd(gitConfig['setThisRepo']);
  1250   }
  1251 };
  1252 
  1253 showCommandHelp = function(cmd) {
  1254   var l_l;
  1255   l_l = COMMAND_MAP[cmd].h.split('\n');
  1256   console.log(l_l[0]);
  1257   l_l = l_l.slice(1);
  1258   console.log(l_l.join('\n').green);
  1259 };
  1260 
  1261 COMMAND_MAP = {
  1262   'install': {
  1263     f: function() {
  1264       thisrepo(offCommands['install']);
  1265     },
  1266     h: 'git off install [thisrepo]\n  setup git config (default global)\n  thisrepo sets up config in current repo'
  1267   },
  1268   'mode': {
  1269     f: function() {
  1270       thisrepo(offCommands['mode']);
  1271     },
  1272     h: 'git off mode [thisrepo] [copy|rsync|scp|http|s3]\n  set/show git off mode'
  1273   },
  1274   'store': {
  1275     f: function() {
  1276       thisrepo(offCommands['store']);
  1277     },
  1278     h: 'git off store [thisrepo] [path]\n  set/show git off store path for copy mode'
  1279   },
  1280   'scp': {
  1281     f: function() {
  1282       thisrepo(offCommands['scp']);
  1283     },
  1284     h: 'git off scp [thisrepo] [host]\n  setup scp config\n  host has format host:path, user@host:path, user@host:port/path\n  Example: localhost:/tmp/offStore'
  1285   },
  1286   'http': {
  1287     f: function() {
  1288       thisrepo(offCommands['http']);
  1289     },
  1290     h: 'git off http [thisrepo] [host]\n  setup http config\n  host has format http://host/path\n  Example: http://localhost/offStore'
  1291   },
  1292   'curl': {
  1293     f: function() {
  1294       thisrepo(offCommands['curl']);
  1295     },
  1296     h: 'git off curl [thisrepo] [options]\n  setup curl config'
  1297   },
  1298   'integrity': {
  1299     f: function() {
  1300       thisrepo(offCommands['integrity']);
  1301     },
  1302     h: 'git off integrity [thisrepo] [enable|disable]\n  set/show git off integrity.\n  when enabled, the SHA of the file received from the store is\n  checked again the SHA of the original file'
  1303   },
  1304   'pem': {
  1305     f: function() {
  1306       thisrepo(offCommands['pem']);
  1307     },
  1308     h: "git off pem [thisrepo] [pathToPrivateKey]\n  set/show git off pem.\n  off.pem is the private key for ssh, rsync and scp\n  set 'offNoValue' to set an empty value (useful when there are multiple configs)"
  1309   },
  1310   'sshoptions': {
  1311     f: function() {
  1312       thisrepo(offCommands['sshoptions']);
  1313     },
  1314     h: 'git off sshoptions [thisrepo] [options]\n  set/show git off sshoptions'
  1315   },
  1316   'scpoptions': {
  1317     f: function() {
  1318       thisrepo(offCommands['scpoptions']);
  1319     },
  1320     h: 'git off scpoptions [thisrepo] [options]\n  set/show git off scpoptions'
  1321   },
  1322   'rsyncoptions': {
  1323     f: function() {
  1324       thisrepo(offCommands['rsyncoptions']);
  1325     },
  1326     h: 'git off rsyncoptions [thisrepo] [options]\n  set/show git off rsyncoptions'
  1327   },
  1328   'scpuser': {
  1329     f: function() {
  1330       thisrepo(offCommands['scpUser']);
  1331     },
  1332     h: 'git off scpuser [thisrepo] [username]\n  setup scp username config'
  1333   },
  1334   'track': {
  1335     f: function() {
  1336       offCommands.track();
  1337     },
  1338     h: "git off track\n  setup gitattribute filters\n  example: git off track '*.bin'\n  without parameter, list git off attributes\n  calls git off install"
  1339   },
  1340   'configAlways': {
  1341     f: function() {
  1342       offCommands.configAlways();
  1343     },
  1344     h: "git off configAlways [''|GIT_OFF_CONFIG|repo|global]\n  '' disable configAlways\n  GIT_OFF_CONFIG load all configurations from $GIT_OFF_CONFIG\n  repo load all configurations from current git repo\n  global load all configurations from global git config\n  set 'offNoValue' to set an empty value"
  1345   },
  1346   's3region': {
  1347     f: function() {
  1348       thisrepo(offCommands['s3region']);
  1349     },
  1350     h: 'git off s3region [thisrepo] [region]\n  setup amazon s3 region for the bucket'
  1351   },
  1352   's3bucket': {
  1353     f: function() {
  1354       thisrepo(offCommands['s3bucket']);
  1355     },
  1356     h: 'git off s3bucket [thisrepo] [bucket]\n  setup amazon s3 bucket'
  1357   },
  1358   'transform': {
  1359     f: function() {
  1360       thisrepo(offCommands['transform']);
  1361     },
  1362     h: 'git off transform [thisrepo] [enable|disable]\n  enable transform in clean and smudge filters'
  1363   },
  1364   'transformTo': {
  1365     f: function() {
  1366       thisrepo(offCommands['transformTo']);
  1367     },
  1368     h: "git off transformTo [thisrepo] ['cmd _1 _2']\n  setup transform command for clear filter\n  When the command is empty the regular transport is performed"
  1369   },
  1370   'transformFrom': {
  1371     f: function() {
  1372       thisrepo(offCommands['transformFrom']);
  1373     },
  1374     h: "git off transformFrom [thisrepo] ['cmd _1 _2']\n  setup transform command for smudge filter\n  When the command is empty the regular transport is performed"
  1375   },
  1376   'clean': {
  1377     f: function() {
  1378       offCommands.clean();
  1379     },
  1380     h: 'git off clean\n  internal filter\n  dont use directly'
  1381   },
  1382   'pre-push': {
  1383     f: function() {
  1384       offCommands.prepush();
  1385     },
  1386     h: 'git off pre-push\n  internal filter\n  dont use directly'
  1387   },
  1388   'smudge': {
  1389     f: function() {
  1390       offCommands.smudge();
  1391     },
  1392     h: 'git off smudge\n  internal filter\n  dont use directly'
  1393   },
  1394   'copyTo': {
  1395     f: function() {
  1396       offCommands.copyTo();
  1397     },
  1398     h: 'git off copyTo [copy|rsync|scp|s3]\n  copy cache to store for specified mode'
  1399   },
  1400   'push': {
  1401     f: function() {
  1402       offCommands.pushTo();
  1403     },
  1404     h: 'git off push\n  copy cache to store for selected mode'
  1405   },
  1406   'clearAll': {
  1407     f: function() {
  1408       offCommands.clearAll();
  1409     },
  1410     h: 'git off clearAll\n  delete store, cache and log'
  1411   },
  1412   'ca': {
  1413     f: function() {
  1414       offCommands.clearAll();
  1415     },
  1416     h: 'git off ca\n  delete store, cache and log'
  1417   },
  1418   'clearCache': {
  1419     f: function() {
  1420       offCommands.clearCache();
  1421     },
  1422     h: 'git off clearCache\n  delete cache in current git'
  1423   },
  1424   'cc': {
  1425     f: function() {
  1426       offCommands.clearCache();
  1427     },
  1428     h: 'git off cc\n  delete cache in current git'
  1429   },
  1430   'clearStore': {
  1431     f: function() {
  1432       offCommands.clearStore();
  1433     },
  1434     h: 'git off clearStore\n  delete store'
  1435   },
  1436   'cs': {
  1437     f: function() {
  1438       offCommands.clearStore();
  1439     },
  1440     h: 'git off ct\n  delete store'
  1441   },
  1442   'clearTmp': {
  1443     f: function() {
  1444       offCommands.clearTmp();
  1445     },
  1446     h: 'git off clearTmp\n  delete tmp in git off cache\n  Useful when transform is enabled'
  1447   },
  1448   'ct': {
  1449     f: function() {
  1450       offCommands.clearTmp();
  1451     },
  1452     h: 'git off cs\n  delete tmp in git off cache\n  Useful when transform is enabled'
  1453   },
  1454   'defaults': {
  1455     f: function() {
  1456       offCommands.defaults();
  1457     },
  1458     h: 'git off defaults\n  shows first time config'
  1459   },
  1460   'env': {
  1461     f: function() {
  1462       offCommands.env();
  1463     },
  1464     h: 'git off env\n  shows config'
  1465   },
  1466   'help': {
  1467     f: function() {
  1468       offCommands.help();
  1469     },
  1470     h: 'git off help [cmd]\n  git off help. Run git off help command to get help for a specific command.'
  1471   }
  1472 };
  1473 
  1474 if (process.argv[2] === void 0) {
  1475   COMMAND_MAP.help.f();
  1476 } else {
  1477   if (COMMAND_MAP[process.argv[2]] === void 0) {
  1478     COMMAND_MAP['help'].f();
  1479   } else {
  1480     COMMAND_MAP[process.argv[2]].f();
  1481   }
  1482 }
  1483 
  1484 
  1485 /*
  1486  * exports for unit tests
  1487  * export everything
  1488  */
  1489 
  1490 module.exports = {
  1491   runtimeConfig: runtimeConfig,
  1492   offDEFAULTS: offDEFAULTS,
  1493   expandHome: expandHome,
  1494   gitConfig: gitConfig,
  1495   offLog: offLog,
  1496   offLogRepo: offLogRepo,
  1497   exec: exec,
  1498   mkdirParents: mkdirParents,
  1499   rmAll: rmAll,
  1500   copy: copy,
  1501   walkSync: walkSync,
  1502   offHelpers: offHelpers,
  1503   transport: transport,
  1504   offCommands: offCommands,
  1505   thisrepo: thisrepo,
  1506   showCommandHelp: showCommandHelp,
  1507   COMMAND_MAP: COMMAND_MAP
  1508 };