git-off

git off handles large files in git repos
git clone https://noulin.net/git/git-off.git
Log | Files | Refs | README

index.js (5813B)


      1 #!/usr/bin/env node
      2 
      3 // External modules
      4 var node_path = require('path');
      5 var node_fs = require('fs');
      6 var _ = require('lodash');
      7 var iconv = require('iconv-lite');
      8 var rimraf = require('rimraf');
      9 
     10 
     11 var sync = module.exports = {};
     12 
     13 // explode built-in fs methods to `fs-more`
     14 // sync.__proto__ = node_fs;
     15 
     16 
     17 // Process specified wildcard glob patterns or filenames against a
     18 // callback, excluding and uniquing files in the result set.
     19 function processPatterns(patterns, fn) {
     20 
     21   // Filepaths to return.
     22   var result = [];
     23   // Iterate over flattened patterns array.
     24   _.flattenDeep(patterns).forEach(function(pattern) {
     25     // If the first character is ! it should be omitted
     26     var exclusion = pattern.indexOf('!') === 0;
     27     // If the pattern is an exclusion, remove the !
     28     if (exclusion) {
     29       pattern = pattern.slice(1);
     30     }
     31     // Find all matching files for this pattern.
     32     var matches = fn(pattern);
     33 
     34     if (exclusion) {
     35       // If an exclusion, remove matching files.
     36       result = _.difference(result, matches);
     37     } else {
     38       // Otherwise add matching files.
     39       result = _.union(result, matches);
     40     }
     41   });
     42 
     43   return result;
     44 };
     45 
     46 // Are descendant path(s) contained within ancestor path? Note: does not test
     47 // if paths actually exist.
     48 sync.doesPathContain = function(ancestor) {
     49   ancestor = node_path.resolve(ancestor);
     50 
     51   var relative;
     52   var i = 1;
     53 
     54   for (; i < arguments.length; i++) {
     55     relative = node_path.relative(node_path.resolve(arguments[i]), ancestor);
     56     if (relative === '' || /\w+/.test(relative)) {
     57       return false;
     58     }
     59   }
     60 
     61   return true;
     62 };
     63 
     64 // @param {Object} options {
     65 //        force: {boolean} force to overridding
     66 // }
     67 sync.copy = function(file, dest, options) {
     68   if (!options) {
     69     options = {};
     70   }
     71 
     72   // Just set encoding as `null` to force the file to R/W as a `Buffer`
     73   options.encoding = null;
     74 
     75   // 'abc/' -> '/xxxx/xxx/abc'
     76   // 'abc.js' -> '/xxx/xxx/abc.js'
     77   file = node_path.resolve(file);
     78 
     79   if (sync.isFile(file)) {
     80     var content = sync.read(file, options);
     81 
     82     if (options.force || !sync.exists(dest)) {
     83       return sync.write(dest, content, options);
     84     }
     85 
     86     return false;
     87 
     88   } else if (sync.isDir(file)) {
     89 
     90     var dir = file;
     91     dest = node_path.resolve(dest);
     92 
     93     sync.expand('**', {
     94 
     95       // to get relative paths to dir
     96       cwd: dir
     97 
     98     }).forEach(function(path) {
     99       var full_path = node_path.join(dir, path);
    100 
    101       if (sync.isFile(full_path)) {
    102         sync.copy(full_path, node_path.join(dest, path), options);
    103       }
    104     });
    105   }
    106 };
    107 
    108 var glob = require('glob');
    109 
    110 // Return an array of all file paths that match the given wildcard patterns.
    111 sync.expand = function(patterns, options) {
    112 
    113   // Use the first argument if it's an Array, otherwise convert the arguments
    114   // object to an array and use that.
    115   patterns = Array.isArray(patterns) ? patterns : [patterns];
    116 
    117   return patterns.length === 0 ? [] :
    118 
    119     processPatterns(patterns, function(pattern) {
    120 
    121       // Find all matching files for this pattern.
    122       return glob.sync(pattern, options);
    123     });
    124 };
    125 
    126 var exists = node_fs.existsSync ?
    127   function(file) {
    128     return node_fs.existsSync(file);
    129   } :
    130 
    131   // if node -v <= 0.6, there's no fs.existsSync method.
    132   function(file) {
    133     try {
    134       node_fs.statSync(file);
    135       return true;
    136     } catch (e) {
    137       return false;
    138     }
    139   };
    140 
    141 
    142 // @returns {boolean} true if the file path exists.
    143 sync.exists = function() {
    144   var filepath = node_path.join.apply(node_path, arguments);
    145   return exists(filepath);
    146 };
    147 
    148 
    149 //@returns true if the file is a symbolic link.
    150 sync.isLink = function() {
    151   var filepath = node_path.join.apply(node_path, arguments);
    152   return exists(filepath) && node_fs.lstatSync(filepath).isSymbolicLink();
    153 };
    154 
    155 // @returns {boolean} true if the path is a directory.
    156 sync.isDir = function() {
    157   var filepath = node_path.join.apply(node_path, arguments);
    158   return exists(filepath) && node_fs.statSync(filepath).isDirectory();
    159 };
    160 
    161 // @returns {boolean} true if the path is a file.
    162 sync.isFile = function() {
    163   var filepath = node_path.join.apply(node_path, arguments);
    164   return exists(filepath) && node_fs.statSync(filepath).isFile();
    165 };
    166 
    167 sync.isPathAbsolute = function() {
    168   var filepath = node_path.join.apply(node_path, arguments);
    169   return node_path.resolve(filepath) === filepath.replace(/[\/\\]+$/, '');
    170 };
    171 
    172 var mkdirp = require('mkdirp');
    173 
    174 sync.mkdir = mkdirp.sync;
    175 
    176 sync.defaultEncoding = 'utf-8';
    177 
    178 sync.read = function(filepath, options) {
    179   if (!options) {
    180     options = {};
    181   }
    182 
    183   var contents;
    184 
    185   contents = node_fs.readFileSync(filepath, options);
    186   // If encoding is not explicitly null, convert from encoded buffer to a
    187   // string. If no encoding was specified, use the default.
    188   if (options.encoding !== null) {
    189     contents = iconv.decode(contents, options.encoding || sync.defaultEncoding);
    190     // Strip any BOM that might exist.
    191     if (contents.charCodeAt(0) === 0xFEFF) {
    192       contents = contents.substring(1);
    193     }
    194   }
    195 
    196   return contents;
    197 };
    198 
    199 
    200 sync.readJSON = function(filepath, options) {
    201   var src = sync.read(filepath, options);
    202   return JSON.parse(src);
    203 };
    204 
    205 // Delete folders and files recursively
    206 sync.remove = function(filepath) {
    207   filepath = String(filepath);
    208 
    209   if (!sync.exists(filepath)) {
    210     return false;
    211   }
    212 
    213   rimraf.sync(filepath);
    214 
    215   return true;
    216 };
    217 
    218 // Write a file.
    219 sync.write = function(filepath, contents, options) {
    220   if (!options) {
    221     options = {};
    222   }
    223 
    224   // Create path, if necessary.
    225   sync.mkdir(node_path.dirname(filepath));
    226 
    227   // If contents is already a Buffer, don't try to encode it. If no encoding
    228   // was specified, use the default.
    229   if (!Buffer.isBuffer(contents)) {
    230     contents = iconv.encode(contents, options.encoding || sync.defaultEncoding);
    231   }
    232 
    233   node_fs.writeFileSync(filepath, contents, options);
    234 
    235   return true;
    236 };