1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | var Fs = require("fs-extra");
|
4 | var Minimist = require("minimist");
|
5 | var Path = require("path");
|
6 | var git_1 = require("./git");
|
7 | var local_storage_1 = require("./local-storage");
|
8 | var paths_1 = require("./paths");
|
9 | var utils_1 = require("./utils");
|
10 |
|
11 | function getRecentCommit(offset, format, grep) {
|
12 | if (typeof offset === 'string') {
|
13 | if (!grep) {
|
14 | grep = format;
|
15 | }
|
16 | format = offset;
|
17 | offset = 0;
|
18 | }
|
19 | var argv = [];
|
20 | if (format) {
|
21 | argv.push("--format=" + format);
|
22 | }
|
23 | if (grep) {
|
24 | argv.push("--grep=" + grep);
|
25 | }
|
26 | return git_1.Git.recentCommit(offset, argv);
|
27 | }
|
28 |
|
29 | function getRecentStepCommit(offset, format) {
|
30 | return getRecentCommit(offset, format, '^Step [0-9]\\+');
|
31 | }
|
32 |
|
33 | function getRecentSuperStepCommit(offset, format) {
|
34 | return getRecentCommit(offset, format, '^Step [0-9]\\+:');
|
35 | }
|
36 |
|
37 | function getRecentSubStepCommit(offset, format) {
|
38 | return getRecentCommit(offset, format, '^Step [0-9]\\+\\.[0-9]\\+:');
|
39 | }
|
40 |
|
41 | function getStepDescriptor(message) {
|
42 | if (message == null) {
|
43 | throw TypeError('A message must be provided');
|
44 | }
|
45 | var match = message.match(/^Step (\d+(?:\.\d+)?)\: ((?:.|\n)*)$/);
|
46 | return match && {
|
47 | number: match[1],
|
48 | message: match[2],
|
49 | type: match[1].split('.')[1] ? 'sub' : 'super',
|
50 | };
|
51 | }
|
52 |
|
53 | function getSuperStepDescriptor(message) {
|
54 | if (message == null) {
|
55 | throw TypeError('A message must be provided');
|
56 | }
|
57 | var match = message.match(/^Step (\d+)\: ((?:.|\n)*)$/);
|
58 | return match && {
|
59 | number: Number(match[1]),
|
60 | message: match[2],
|
61 | };
|
62 | }
|
63 |
|
64 | function getSubStepDescriptor(message) {
|
65 | if (message == null) {
|
66 | throw TypeError('A message must be provided');
|
67 | }
|
68 | var match = message.match(/^Step ((\d+)\.(\d+))\: ((?:.|\n)*)$/);
|
69 | return match && {
|
70 | number: match[1],
|
71 | superNumber: Number(match[2]),
|
72 | subNumber: Number(match[3]),
|
73 | message: match[4],
|
74 | };
|
75 | }
|
76 |
|
77 | function pushStep(message, options) {
|
78 | var step = getNextStep();
|
79 | commitStep(step, message, options);
|
80 |
|
81 | local_storage_1.localStorage.setItem('REBASE_NEW_STEP', step);
|
82 | }
|
83 |
|
84 | function popStep() {
|
85 | var headHash = git_1.Git(['rev-parse', 'HEAD']);
|
86 | var rootHash = git_1.Git.rootHash();
|
87 | if (headHash === rootHash) {
|
88 | throw Error("Can't remove root");
|
89 | }
|
90 | var removedCommitMessage = git_1.Git.recentCommit(['--format=%s']);
|
91 | var stepDescriptor = getStepDescriptor(removedCommitMessage);
|
92 | git_1.Git.print(['reset', '--hard', 'HEAD~1']);
|
93 |
|
94 | if (stepDescriptor) {
|
95 | local_storage_1.localStorage.setItem('REBASE_NEW_STEP', getCurrentStep());
|
96 |
|
97 | if (ensureStepMap()) {
|
98 | updateStepMap('remove', { step: stepDescriptor.number });
|
99 | }
|
100 |
|
101 |
|
102 | if (stepDescriptor.type === 'super' && !git_1.Git.rebasing()) {
|
103 | var branch = git_1.Git.activeBranchName();
|
104 | git_1.Git(['branch', '-D', branch + "-step" + stepDescriptor.number]);
|
105 | }
|
106 | }
|
107 | else {
|
108 | console.warn('Removed commit was not a step');
|
109 | return;
|
110 | }
|
111 | }
|
112 |
|
113 | function tagStep(message) {
|
114 | var step = getNextSuperStep();
|
115 | var tag = "step" + step;
|
116 | var manualFile = tag + ".tmpl";
|
117 | var manualTemplatePath = Path.resolve(paths_1.Paths.manuals.templates, manualFile);
|
118 | Fs.ensureDirSync(paths_1.Paths.manuals.templates);
|
119 | Fs.ensureDirSync(paths_1.Paths.manuals.views);
|
120 | Fs.writeFileSync(manualTemplatePath, '');
|
121 | git_1.Git(['add', manualTemplatePath]);
|
122 | commitStep(step, message);
|
123 |
|
124 | if (!git_1.Git.rebasing()) {
|
125 | var branch = git_1.Git.activeBranchName();
|
126 |
|
127 | git_1.Git(['branch', branch + "-step" + step]);
|
128 | }
|
129 |
|
130 | local_storage_1.localStorage.setItem('REBASE_NEW_STEP', step);
|
131 | }
|
132 |
|
133 | function getStepBase(step) {
|
134 | if (!step) {
|
135 | var message = getRecentStepCommit('%s');
|
136 | if (!message) {
|
137 | return '--root';
|
138 | }
|
139 | step = getStepDescriptor(message).number;
|
140 | }
|
141 | if (step === 'root') {
|
142 | return '--root';
|
143 | }
|
144 | var hash = git_1.Git.recentCommit([
|
145 | "--grep=^Step " + step + ":",
|
146 | '--format=%h',
|
147 | ]);
|
148 | if (!hash) {
|
149 | throw Error('Step not found');
|
150 | }
|
151 | return hash + "~1";
|
152 | }
|
153 |
|
154 | function editStep(steps, options) {
|
155 | if (options === void 0) { options = {}; }
|
156 | var rootSha1 = git_1.Git.rootHash();
|
157 | var allSteps = getAllSteps();
|
158 | steps = [].concat(steps).filter(Boolean);
|
159 |
|
160 |
|
161 | steps = steps.reduce(function (flattened, step) {
|
162 | var range = step.match(/(\d+(?:\.\d+)?)?\.\.(?:\.+)?(\d+(?:\.\d+)?)?/);
|
163 | if (!range) {
|
164 | return flattened.concat(step);
|
165 | }
|
166 | var start = range[1] || 'root';
|
167 | var end = range[2] || allSteps[allSteps.length - 1];
|
168 | var startIndex = allSteps.findIndex(function (s) { return s === start; });
|
169 | var endIndex = allSteps.findIndex(function (s) { return s === end; });
|
170 | if (startIndex === -1) {
|
171 | startIndex = 0;
|
172 | }
|
173 | if (endIndex === -1) {
|
174 | startIndex = Infinity;
|
175 | }
|
176 | return flattened.concat(allSteps.slice(startIndex, endIndex + 1));
|
177 | }, []);
|
178 |
|
179 | steps = steps.map(function (step) {
|
180 |
|
181 | if (/^\d{1,5}(\.\d+)?$/.test(step) || step === 'root') {
|
182 | return step;
|
183 | }
|
184 | if (step === rootSha1) {
|
185 | return 'root';
|
186 | }
|
187 | var commitMessage = git_1.Git(['log', step, '-1', '--format=%s']);
|
188 | var descriptor = getStepDescriptor(commitMessage);
|
189 | return descriptor && descriptor.number;
|
190 | }).filter(Boolean);
|
191 | steps = steps.slice().sort(function (a, b) {
|
192 | var _a = a.split('.').concat('Infinity'), superA = _a[0], subA = _a[1];
|
193 | var _b = b.split('.').concat('Infinity'), superB = _b[0], subB = _b[1];
|
194 |
|
195 | if (a === 'root') {
|
196 | return -1;
|
197 | }
|
198 | if (b === 'root') {
|
199 | return 1;
|
200 | }
|
201 |
|
202 | return ((superA - superB) ||
|
203 | (subA - subB));
|
204 | });
|
205 |
|
206 | var base = getStepBase(steps[0]);
|
207 |
|
208 |
|
209 | if (!steps.length && base === '--root') {
|
210 | steps[0] = 'root';
|
211 | }
|
212 | var argv = [paths_1.Paths.tortilla.editor, 'edit'].concat(steps);
|
213 |
|
214 | if (options.udiff != null) {
|
215 | argv.push('--udiff');
|
216 | }
|
217 |
|
218 | if (options.udiff) {
|
219 | argv.push(options.udiff.toString());
|
220 | }
|
221 |
|
222 |
|
223 | if (process.env.TORTILLA_SUBMODULE_CWD) {
|
224 | local_storage_1.localStorage.setItem('SUBMODULE_CWD', process.env.TORTILLA_SUBMODULE_CWD);
|
225 | }
|
226 | git_1.Git.print(['rebase', '-i', base, '--keep-empty'], {
|
227 | env: {
|
228 | GIT_SEQUENCE_EDITOR: "node " + argv.join(' '),
|
229 | },
|
230 | });
|
231 | }
|
232 |
|
233 | function sortStep(step) {
|
234 |
|
235 | if (!step) {
|
236 | step = getRecentStepCommit('%s');
|
237 | step = getStepDescriptor(step);
|
238 | step = step ? step.number : 'root';
|
239 | }
|
240 | var newStep;
|
241 | var oldStep;
|
242 | var base;
|
243 |
|
244 | if (step === 'root') {
|
245 | newStep = '1';
|
246 | oldStep = 'root';
|
247 | base = '--root';
|
248 | }
|
249 | else {
|
250 | newStep = step.split('.').map(Number)[0];
|
251 | oldStep = newStep - 1 || 'root';
|
252 | newStep = newStep + "." + 1;
|
253 | base = getStepBase(newStep);
|
254 | }
|
255 |
|
256 | local_storage_1.localStorage.setItem('REBASE_NEW_STEP', newStep);
|
257 | local_storage_1.localStorage.setItem('REBASE_OLD_STEP', oldStep);
|
258 | git_1.Git.print(['rebase', '-i', base, '--keep-empty'], {
|
259 | env: {
|
260 | GIT_SEQUENCE_EDITOR: "node " + paths_1.Paths.tortilla.editor + " sort",
|
261 | },
|
262 | });
|
263 | }
|
264 |
|
265 | function rewordStep(step, message) {
|
266 | var base = getStepBase(step);
|
267 | var argv = [paths_1.Paths.tortilla.editor, 'reword'];
|
268 | if (message) {
|
269 | argv.push('-m', "\"" + message + "\"");
|
270 | }
|
271 | git_1.Git.print(['rebase', '-i', base, '--keep-empty'], {
|
272 | env: {
|
273 | GIT_SEQUENCE_EDITOR: "node " + argv.join(' '),
|
274 | },
|
275 | });
|
276 | }
|
277 |
|
278 | function showStep(step) {
|
279 | var args = [];
|
280 | for (var _i = 1; _i < arguments.length; _i++) {
|
281 | args[_i - 1] = arguments[_i];
|
282 | }
|
283 | assertStep(step);
|
284 | step = step.split('.').join('\\.');
|
285 | var hash = git_1.Git(['log', "--grep=^Step " + step, '--format=%H']);
|
286 | if (!hash) {
|
287 | throw Error('Step not found');
|
288 | }
|
289 | git_1.Git.print(['show', hash].concat(args));
|
290 | }
|
291 |
|
292 | function assertStep(step, silent) {
|
293 | if (silent === void 0) { silent = false; }
|
294 | if (typeof step !== 'string' && typeof step !== 'number') {
|
295 | if (silent) {
|
296 | return false;
|
297 | }
|
298 | throw TypeError('Provided argument is not of type string or number');
|
299 | }
|
300 | step = step.toString();
|
301 | if (!/\d+/.test(step) && !/\d+\.\d+/.test(step)) {
|
302 | if (silent) {
|
303 | return false;
|
304 | }
|
305 | throw TypeError('Provided argument is not a step');
|
306 | }
|
307 | return true;
|
308 | }
|
309 |
|
310 | function commitStep(step, message, options) {
|
311 | if (options === void 0) { options = {}; }
|
312 | var argv = ['commit'];
|
313 | if (message) {
|
314 | argv.push('-m', message);
|
315 | }
|
316 | if (options.allowEmpty) {
|
317 | argv.push('--allow-empty');
|
318 | }
|
319 |
|
320 | local_storage_1.localStorage.setItem('HOOK_STEP', step);
|
321 | try {
|
322 |
|
323 | git_1.Git.print(argv);
|
324 | }
|
325 | catch (err) {
|
326 |
|
327 | local_storage_1.localStorage.removeItem('HOOK_STEP');
|
328 | throw err;
|
329 | }
|
330 | }
|
331 |
|
332 | function getCurrentStep() {
|
333 |
|
334 | var recentStepCommit = getRecentStepCommit('%s');
|
335 | if (!recentStepCommit) {
|
336 | return 'root';
|
337 | }
|
338 |
|
339 | var descriptor = getStepDescriptor(recentStepCommit);
|
340 | if (!descriptor) {
|
341 | return 'root';
|
342 | }
|
343 | return descriptor.number;
|
344 | }
|
345 |
|
346 | function getCurrentSuperStep() {
|
347 |
|
348 | var recentStepCommit = getRecentSuperStepCommit('%s');
|
349 | if (!recentStepCommit) {
|
350 | return 'root';
|
351 | }
|
352 |
|
353 | var descriptor = getSuperStepDescriptor(recentStepCommit);
|
354 | if (!descriptor) {
|
355 | return 'root';
|
356 | }
|
357 | return descriptor.number;
|
358 | }
|
359 |
|
360 | function getNextStep(offset) {
|
361 |
|
362 | var stepCommitMessage = getRecentStepCommit(offset, '%s');
|
363 | var followedByStep = !!stepCommitMessage;
|
364 |
|
365 | if (!followedByStep) {
|
366 | return '1.1';
|
367 | }
|
368 |
|
369 | var stepDescriptor = getStepDescriptor(stepCommitMessage);
|
370 | var stepNumbers = stepDescriptor.number.split('.');
|
371 | var superStepNumber = Number(stepNumbers[0]);
|
372 | var subStepNumber = Number(stepNumbers[1]);
|
373 | var isSuperStep = !subStepNumber;
|
374 | if (!offset) {
|
375 |
|
376 | if (isSuperStep) {
|
377 | return superStepNumber + 1 + "." + 1;
|
378 | }
|
379 |
|
380 | return superStepNumber + "." + (subStepNumber + 1);
|
381 | }
|
382 |
|
383 | var nextStepCommitMessage = getRecentStepCommit(offset - 1, '%s');
|
384 | var nextStepDescriptor = getStepDescriptor(nextStepCommitMessage);
|
385 | var nextStepNumbers = nextStepDescriptor.number.split('.');
|
386 | var nextSubStepNumber = Number(nextStepNumbers[1]);
|
387 | var isNextSuperStep = !nextSubStepNumber;
|
388 | if (isNextSuperStep) {
|
389 |
|
390 | if (isSuperStep) {
|
391 | return (superStepNumber + 1).toString();
|
392 | }
|
393 |
|
394 | return superStepNumber.toString();
|
395 | }
|
396 |
|
397 | if (isSuperStep) {
|
398 | return superStepNumber + 1 + "." + 1;
|
399 | }
|
400 |
|
401 | return superStepNumber + "." + (subStepNumber + 1);
|
402 | }
|
403 |
|
404 | function getNextSuperStep(offset) {
|
405 | return getNextStep(offset).split('.')[0];
|
406 | }
|
407 |
|
408 | function initializeStepMap(pending) {
|
409 | var map = git_1.Git([
|
410 | 'log', '--format=%s', '--grep=^Step [0-9]\\+',
|
411 | ])
|
412 | .split('\n')
|
413 | .filter(Boolean)
|
414 | .reduce(function (m, subject) {
|
415 | var num = getStepDescriptor(subject).number;
|
416 | m[num] = num;
|
417 | return m;
|
418 | }, {});
|
419 | local_storage_1.localStorage.setItem('STEP_MAP', JSON.stringify(map));
|
420 | if (pending) {
|
421 | local_storage_1.localStorage.setItem('STEP_MAP_PENDING', true);
|
422 | }
|
423 | else {
|
424 | local_storage_1.localStorage.removeItem('STEP_MAP_PENDING');
|
425 | }
|
426 | }
|
427 |
|
428 | function getStepMap(submoduleCwd, checkPending) {
|
429 | var localStorage;
|
430 |
|
431 | if (submoduleCwd) {
|
432 | localStorage = local_storage_1.localStorage.create(submoduleCwd);
|
433 | }
|
434 | else {
|
435 | localStorage = local_storage_1.localStorage;
|
436 | }
|
437 | if (ensureStepMap(submoduleCwd, checkPending)) {
|
438 | return JSON.parse(localStorage.getItem('STEP_MAP'));
|
439 | }
|
440 | }
|
441 |
|
442 |
|
443 | function ensureStepMap(submoduleCwd, checkPending) {
|
444 |
|
445 | if (checkPending && local_storage_1.localStorage.getItem('STEP_MAP_PENDING')) {
|
446 | return false;
|
447 | }
|
448 | var paths;
|
449 |
|
450 | if (submoduleCwd) {
|
451 | paths = paths_1.Paths.resolveProject(submoduleCwd);
|
452 | }
|
453 | else {
|
454 | paths = paths_1.Paths;
|
455 | }
|
456 | return utils_1.Utils.exists(Path.resolve(paths.storage, 'STEP_MAP'), 'file');
|
457 | }
|
458 | function disposeStepMap() {
|
459 | local_storage_1.localStorage.deleteItem('STEP_MAP');
|
460 | local_storage_1.localStorage.deleteItem('STEP_MAP_PENDING');
|
461 | }
|
462 | function updateStepMap(type, payload) {
|
463 | var map = getStepMap();
|
464 | switch (type) {
|
465 | case 'remove':
|
466 | delete map[payload.step];
|
467 | break;
|
468 | case 'reset':
|
469 | map[payload.oldStep] = payload.newStep;
|
470 | break;
|
471 | }
|
472 | local_storage_1.localStorage.setItem('STEP_MAP', JSON.stringify(map));
|
473 | }
|
474 |
|
475 | function getAllSteps() {
|
476 | var allSteps = git_1.Git(['log', '--grep=^Step [0-9]\\+.\\?[0-9]*:', '--format=%s'])
|
477 | .split('\n')
|
478 | .map(function (message) { return getStepDescriptor(message); })
|
479 | .filter(Boolean)
|
480 | .map(function (descriptor) { return descriptor.number; })
|
481 | .reverse();
|
482 | allSteps.unshift('root');
|
483 | return allSteps;
|
484 | }
|
485 |
|
486 |
|
487 |
|
488 | (function () {
|
489 | if (require.main !== module) {
|
490 | return;
|
491 | }
|
492 | var argv = Minimist(process.argv.slice(2), {
|
493 | string: ['_', 'message', 'm'],
|
494 | boolean: ['root', 'udiff', 'allow-empty'],
|
495 | });
|
496 | var method = argv._[0];
|
497 | var step = argv._[1];
|
498 | var message = argv.message || argv.m;
|
499 | var root = argv.root;
|
500 | var allowEmpty = argv['allow-empty'];
|
501 | var udiff = argv.udiff;
|
502 | if (!step && root) {
|
503 | step = 'root';
|
504 | }
|
505 | var options = {
|
506 | allowEmpty: allowEmpty,
|
507 | udiff: udiff,
|
508 | };
|
509 | switch (method) {
|
510 | case 'push':
|
511 | return pushStep(message, options);
|
512 | case 'pop':
|
513 | return popStep();
|
514 | case 'tag':
|
515 | return tagStep(message);
|
516 | case 'edit':
|
517 | return editStep(step, options);
|
518 | case 'sort':
|
519 | return sortStep(step);
|
520 | case 'reword':
|
521 | return rewordStep(step, message);
|
522 | }
|
523 | })();
|
524 | exports.Step = {
|
525 | push: pushStep,
|
526 | pop: popStep,
|
527 | tag: tagStep,
|
528 | edit: editStep,
|
529 | sort: sortStep,
|
530 | reword: rewordStep,
|
531 | show: showStep,
|
532 | assert: assertStep,
|
533 | commit: commitStep,
|
534 | current: getCurrentStep,
|
535 | currentSuper: getCurrentSuperStep,
|
536 | next: getNextStep,
|
537 | nextSuper: getNextSuperStep,
|
538 | base: getStepBase,
|
539 | recentCommit: getRecentStepCommit,
|
540 | recentSuperCommit: getRecentSuperStepCommit,
|
541 | recentSubCommit: getRecentSubStepCommit,
|
542 | descriptor: getStepDescriptor,
|
543 | superDescriptor: getSuperStepDescriptor,
|
544 | subDescriptor: getSubStepDescriptor,
|
545 | initializeStepMap: initializeStepMap,
|
546 | getStepMap: getStepMap,
|
547 | ensureStepMap: ensureStepMap,
|
548 | disposeStepMap: disposeStepMap,
|
549 | updateStepMap: updateStepMap,
|
550 | all: getAllSteps,
|
551 | };
|
552 |
|
\ | No newline at end of file |