-
Notifications
You must be signed in to change notification settings - Fork 1
/
git-walk.js
executable file
·142 lines (111 loc) · 3.61 KB
/
git-walk.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#!/usr/bin/env node
var Parser = require('posix-getopt').BasicParser;
var Queue = require('async').queue;
var each = require('async').each;
var exec = require('child_process').exec;
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;
// May need graceful-fs, might even need graceful-exec...
var $0 = path.basename(process.argv[1]);
var HELP = fs.readFileSync(require.resolve('./help.txt'), 'utf-8')
.replace(/%MAIN%/g, $0);
var parser = new Parser([
':',
'h(help)',
'v(verbose)',
'q(quiet)',
'w:(where)',
'1(serial)',
'p(parallel)',
'n:(concurrency)',
].join(''), process.argv);
var option;
var verbose = 1;
var where = process.cwd();
var concurrency = 20;
while ((option = parser.getopt()) !== undefined) {
switch (option.option) {
case 'h': return console.log('%s', HELP);
case 'v': verbose += 1; break;
case 'q': verbose = 0; break;
case 'w': where = option.optarg; break;
case '1': concurrency = 1; break;
case 'p': concurrency = 20; break;
case 'n': concurrency = Number(option.optarg) || concurrency; break;
default:
console.error('Invalid usage (near options \'%s\'), try \'%s --help\'',
option.optopt, $0);
process.exit(1);
}
}
var debug = verbose > 1 ? console.log : function() {};
var args = process.argv.slice(parser.optind());
if (!args.length)
args = ['git', 'status', '--short', '-b']
var cmd = args.join(' ');
var arg0 = args.shift();
var queue = Queue(execute, concurrency);
debug('where: %s', where);
debug('concurrency: %s', concurrency);
debug('cmd: %s', cmd);
follow(process.cwd(), where);
function follow(from, dir, done) {
var where = path.resolve(from, dir);
debug('follow from %j dir %j where %j', from, dir, where);
if (!done) done = function() {};
fs.readdir(where, function(err, dirs) {
if (err) {
// Wasn't a directory: ENOTDIR
// Dangling symlink: ENOENT
if (err.code !== 'ENOTDIR' && err.code !== 'ENOENT')
console.error('readdir failed: %s', err.message);
return done();
}
if (dirs.indexOf('.git') >= 0) {
push(where);
return done();
}
debug('where %j dirs: %j', dirs);
each(dirs, follow.bind(null, where), done);
});
}
function push(where) {
queue.push(where);
}
function execute(where, done) {
debug('executor where %j', where);
var options = {
cwd: where,
env: process.env,
encoding: 'utf8',
stdio: 'inherit',
// maxBuffer: 200 * 1024, // default, probably enough
};
// git colors output when its writing directly to the console, and those
// colors are nice, but in parallel mode, direct writing just causes the
// output to be intermixed on the screen. We can't have our cake and eat it
// too. We have to choose parallel execution, where output is buffered so it
// can be written atomically, or serial execution, where output is written
// directly to console and colored.
//
// Buffered is done with exec, direct is done with spawn.
if (concurrency === 1) {
if (verbose) console.log('cd %s; `%s`', where, cmd);
return spawn(arg0, args, options).on('exit', onExit);
}
exec(cmd, options, function(err, stdout, stderr) {
if (err) return onExit(err.code, err.signal);
if (verbose) console.log('cd %s; `%s`', where, cmd);
process.stdout.write(stdout);
process.stderr.write(stderr);
return onExit(0);
});
function onExit(code, signal) {
if (code === 0) return done();
console.error('cd %s: `%s` failed with %s', where, cmd, signal || code);
if (signal)
process.kill(process.pid, err.signal);
return done();
}
}