UNPKG

17.5 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3var Fs = require("fs-extra");
4var Minimist = require("minimist");
5var Path = require("path");
6var git_1 = require("./git");
7var local_storage_1 = require("./local-storage");
8var paths_1 = require("./paths");
9var utils_1 = require("./utils");
10// Get recent commit by specified arguments
11function 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// Get the recent step commit
29function getRecentStepCommit(offset, format) {
30 return getRecentCommit(offset, format, '^Step [0-9]\\+');
31}
32// Get the recent super step commit
33function getRecentSuperStepCommit(offset, format) {
34 return getRecentCommit(offset, format, '^Step [0-9]\\+:');
35}
36// Get the recent sub step commit
37function getRecentSubStepCommit(offset, format) {
38 return getRecentCommit(offset, format, '^Step [0-9]\\+\\.[0-9]\\+:');
39}
40// Extract step json from message
41function 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// Extract super step json from message
53function 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// Extract sub step json from message
64function 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// Push a new step with the provided message
77function pushStep(message, options) {
78 var step = getNextStep();
79 commitStep(step, message, options);
80 // Meta-data for step editing
81 local_storage_1.localStorage.setItem('REBASE_NEW_STEP', step);
82}
83// Pop the last step
84function 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 // Meta-data for step editing
94 if (stepDescriptor) {
95 local_storage_1.localStorage.setItem('REBASE_NEW_STEP', getCurrentStep());
96 // This will be used later on to update the manuals
97 if (ensureStepMap()) {
98 updateStepMap('remove', { step: stepDescriptor.number });
99 }
100 // Delete branch referencing the super step unless we're rebasing, in which case the
101 // branches will be reset automatically at the end of the rebase
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// Finish the current with the provided message and tag it
113function 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 // If we're in edit mode all the branches will be set after the rebase
124 if (!git_1.Git.rebasing()) {
125 var branch = git_1.Git.activeBranchName();
126 // This branch will be used to run integration testing
127 git_1.Git(['branch', branch + "-step" + step]);
128 }
129 // Meta-data for step editing
130 local_storage_1.localStorage.setItem('REBASE_NEW_STEP', step);
131}
132// Get the hash of the step followed by ~1, mostly useful for a rebase
133function 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// Edit the provided step
154function editStep(steps, options) {
155 if (options === void 0) { options = {}; }
156 var rootSha1 = git_1.Git.rootHash();
157 // Map git-refs to step indexes
158 steps = [].concat(steps).filter(Boolean).map(function (step) {
159 // If an index was provided, return it; otherwise try to find the index by SHA1
160 if (/^\d{1,5}(\.\d+)?$/.test(step) || step === 'root') {
161 return step;
162 }
163 if (step === rootSha1) {
164 return 'root';
165 }
166 var commitMessage = git_1.Git(['log', step, '-1', '--format=%s']);
167 var descriptor = getStepDescriptor(commitMessage);
168 return descriptor && descriptor.number;
169 }).filter(Boolean);
170 if (steps instanceof Array) {
171 steps = steps.slice().sort(function (a, b) {
172 var _a = a.split('.').concat('Infinity'), superA = _a[0], subA = _a[1];
173 var _b = b.split('.').concat('Infinity'), superB = _b[0], subB = _b[1];
174 // Always put the root on top
175 if (a === 'root') {
176 return -1;
177 }
178 if (b === 'root') {
179 return 1;
180 }
181 // Put first steps first
182 return ((superA - superB) ||
183 (subA - subB));
184 });
185 }
186 else {
187 steps = [steps];
188 }
189 // The would always have to start from the first step
190 var base = getStepBase(steps[0]);
191 // '--root' might be fetched in case no steps where provided. We need to fill up
192 // this missing information in the steps array
193 if (!steps.length && base === '--root') {
194 steps[0] = 'root';
195 }
196 var argv = [paths_1.Paths.tortilla.editor, 'edit'].concat(steps);
197 // Update diffSteps
198 if (options.udiff != null) {
199 argv.push('--udiff');
200 }
201 // Update diffSteps in another repo
202 if (options.udiff) {
203 argv.push(options.udiff.toString());
204 }
205 // Storing locally so it can be used in further processes
206 // Indicates that this operation is hooked into a submodule
207 if (process.env.TORTILLA_SUBMODULE_CWD) {
208 local_storage_1.localStorage.setItem('SUBMODULE_CWD', process.env.TORTILLA_SUBMODULE_CWD);
209 }
210 git_1.Git.print(['rebase', '-i', base, '--keep-empty'], {
211 env: {
212 GIT_SEQUENCE_EDITOR: "node " + argv.join(' '),
213 },
214 });
215}
216// Adjust all the step indexes from the provided step
217function sortStep(step) {
218 // If no step was provided, take the most recent one
219 if (!step) {
220 step = getRecentStepCommit('%s');
221 step = getStepDescriptor(step);
222 step = step ? step.number : 'root';
223 }
224 var newStep;
225 var oldStep;
226 var base;
227 // If root, make sure to sort all step indexes since the beginning of history
228 if (step === 'root') {
229 newStep = '1';
230 oldStep = 'root';
231 base = '--root';
232 }
233 else { // Else, adjust only the steps in the given super step
234 newStep = step.split('.').map(Number)[0];
235 oldStep = newStep - 1 || 'root';
236 newStep = newStep + "." + 1;
237 base = getStepBase(newStep);
238 }
239 // Setting local storage variables so re-sortment could be done properly
240 local_storage_1.localStorage.setItem('REBASE_NEW_STEP', newStep);
241 local_storage_1.localStorage.setItem('REBASE_OLD_STEP', oldStep);
242 git_1.Git.print(['rebase', '-i', base, '--keep-empty'], {
243 env: {
244 GIT_SEQUENCE_EDITOR: "node " + paths_1.Paths.tortilla.editor + " sort",
245 },
246 });
247}
248// Reword the provided step with the provided message
249function rewordStep(step, message) {
250 var base = getStepBase(step);
251 var argv = [paths_1.Paths.tortilla.editor, 'reword'];
252 if (message) {
253 argv.push('-m', "\"" + message + "\"");
254 }
255 git_1.Git.print(['rebase', '-i', base, '--keep-empty'], {
256 env: {
257 GIT_SEQUENCE_EDITOR: "node " + argv.join(' '),
258 },
259 });
260}
261// Run git-show for given step index
262function showStep(step) {
263 var args = [];
264 for (var _i = 1; _i < arguments.length; _i++) {
265 args[_i - 1] = arguments[_i];
266 }
267 assertStep(step);
268 step = step.split('.').join('\\.');
269 var hash = git_1.Git(['log', "--grep=^Step " + step, '--format=%H']);
270 if (!hash) {
271 throw Error('Step not found');
272 }
273 git_1.Git.print(['show', hash].concat(args));
274}
275// Asserts whether provided string is a step index or not
276function assertStep(step, silent) {
277 if (silent === void 0) { silent = false; }
278 if (typeof step !== 'string' && typeof step !== 'number') {
279 if (silent) {
280 return false;
281 }
282 throw TypeError('Provided argument is not of type string or number');
283 }
284 step = step.toString();
285 if (!/\d+/.test(step) && !/\d+\.\d+/.test(step)) {
286 if (silent) {
287 return false;
288 }
289 throw TypeError('Provided argument is not a step');
290 }
291 return true;
292}
293// Add a new commit of the provided step with the provided message
294function commitStep(step, message, options) {
295 if (options === void 0) { options = {}; }
296 var argv = ['commit'];
297 if (message) {
298 argv.push('-m', message);
299 }
300 if (options.allowEmpty) {
301 argv.push('--allow-empty');
302 }
303 // Specified step is gonna be used for when forming the commit message
304 local_storage_1.localStorage.setItem('HOOK_STEP', step);
305 try {
306 // commit
307 git_1.Git.print(argv);
308 }
309 catch (err) {
310 // Clearing storage to prevent conflicts with upcoming commits
311 local_storage_1.localStorage.removeItem('HOOK_STEP');
312 throw err;
313 }
314}
315// Get the current step
316function getCurrentStep() {
317 // Probably root commit
318 var recentStepCommit = getRecentStepCommit('%s');
319 if (!recentStepCommit) {
320 return 'root';
321 }
322 // Cover unexpected behavior
323 var descriptor = getStepDescriptor(recentStepCommit);
324 if (!descriptor) {
325 return 'root';
326 }
327 return descriptor.number;
328}
329// Get the current super step
330function getCurrentSuperStep() {
331 // Probably root commit
332 var recentStepCommit = getRecentSuperStepCommit('%s');
333 if (!recentStepCommit) {
334 return 'root';
335 }
336 // Cover unexpected behavior
337 var descriptor = getSuperStepDescriptor(recentStepCommit);
338 if (!descriptor) {
339 return 'root';
340 }
341 return descriptor.number;
342}
343// Get the next step
344function getNextStep(offset) {
345 // Fetch data about recent step commit
346 var stepCommitMessage = getRecentStepCommit(offset, '%s');
347 var followedByStep = !!stepCommitMessage;
348 // If no previous steps found return the first one
349 if (!followedByStep) {
350 return '1.1';
351 }
352 // Fetch data about current step
353 var stepDescriptor = getStepDescriptor(stepCommitMessage);
354 var stepNumbers = stepDescriptor.number.split('.');
355 var superStepNumber = Number(stepNumbers[0]);
356 var subStepNumber = Number(stepNumbers[1]);
357 var isSuperStep = !subStepNumber;
358 if (!offset) {
359 // If this is a super step return the first sub step of a new step
360 if (isSuperStep) {
361 return superStepNumber + 1 + "." + 1;
362 }
363 // Else, return the next step as expected
364 return superStepNumber + "." + (subStepNumber + 1);
365 }
366 // Fetch data about next step
367 var nextStepCommitMessage = getRecentStepCommit(offset - 1, '%s');
368 var nextStepDescriptor = getStepDescriptor(nextStepCommitMessage);
369 var nextStepNumbers = nextStepDescriptor.number.split('.');
370 var nextSubStepNumber = Number(nextStepNumbers[1]);
371 var isNextSuperStep = !nextSubStepNumber;
372 if (isNextSuperStep) {
373 // If this is a super step return the next super step right away
374 if (isSuperStep) {
375 return (superStepNumber + 1).toString();
376 }
377 // Else, return the current super step
378 return superStepNumber.toString();
379 }
380 // If this is a super step return the first sub step of the next step
381 if (isSuperStep) {
382 return superStepNumber + 1 + "." + 1;
383 }
384 // Else, return the next step as expected
385 return superStepNumber + "." + (subStepNumber + 1);
386}
387// Get the next super step
388function getNextSuperStep(offset) {
389 return getNextStep(offset).split('.')[0];
390}
391// Pending flag indicates that this step map will be used in another tortilla repo
392function initializeStepMap(pending) {
393 var map = git_1.Git([
394 'log', '--format=%s', '--grep=^Step [0-9]\\+',
395 ])
396 .split('\n')
397 .filter(Boolean)
398 .reduce(function (m, subject) {
399 var num = getStepDescriptor(subject).number;
400 m[num] = num;
401 return m;
402 }, {});
403 local_storage_1.localStorage.setItem('STEP_MAP', JSON.stringify(map));
404 if (pending) {
405 local_storage_1.localStorage.setItem('STEP_MAP_PENDING', true);
406 }
407 else {
408 local_storage_1.localStorage.removeItem('STEP_MAP_PENDING');
409 }
410}
411// First argument represents the module we would like to read the steps map from
412function getStepMap(submoduleCwd, checkPending) {
413 var localStorage;
414 // In case this process was launched from a submodule
415 if (submoduleCwd) {
416 localStorage = local_storage_1.localStorage.create(submoduleCwd);
417 }
418 else {
419 localStorage = local_storage_1.localStorage;
420 }
421 if (ensureStepMap(submoduleCwd, checkPending)) {
422 return JSON.parse(localStorage.getItem('STEP_MAP'));
423 }
424}
425// Provided argument will run an extra condition to check whether the pending flag
426// exists or not
427function ensureStepMap(submoduleCwd, checkPending) {
428 // Step map shouldn't be used in this process
429 if (checkPending && local_storage_1.localStorage.getItem('STEP_MAP_PENDING')) {
430 return false;
431 }
432 var paths;
433 // In case this process was launched from a submodule
434 if (submoduleCwd) {
435 paths = paths_1.Paths.resolveProject(submoduleCwd);
436 }
437 else {
438 paths = paths_1.Paths;
439 }
440 return utils_1.Utils.exists(Path.resolve(paths.storage, 'STEP_MAP'), 'file');
441}
442function disposeStepMap() {
443 local_storage_1.localStorage.deleteItem('STEP_MAP');
444 local_storage_1.localStorage.deleteItem('STEP_MAP_PENDING');
445}
446function updateStepMap(type, payload) {
447 var map = getStepMap();
448 switch (type) {
449 case 'remove':
450 delete map[payload.step];
451 break;
452 case 'reset':
453 map[payload.oldStep] = payload.newStep;
454 break;
455 }
456 local_storage_1.localStorage.setItem('STEP_MAP', JSON.stringify(map));
457}
458/**
459 Contains step related utilities.
460 */
461(function () {
462 if (require.main !== module) {
463 return;
464 }
465 var argv = Minimist(process.argv.slice(2), {
466 string: ['_', 'message', 'm'],
467 boolean: ['root', 'udiff', 'allow-empty'],
468 });
469 var method = argv._[0];
470 var step = argv._[1];
471 var message = argv.message || argv.m;
472 var root = argv.root;
473 var allowEmpty = argv['allow-empty'];
474 var udiff = argv.udiff;
475 if (!step && root) {
476 step = 'root';
477 }
478 var options = {
479 allowEmpty: allowEmpty,
480 udiff: udiff,
481 };
482 switch (method) {
483 case 'push':
484 return pushStep(message, options);
485 case 'pop':
486 return popStep();
487 case 'tag':
488 return tagStep(message);
489 case 'edit':
490 return editStep(step, options);
491 case 'sort':
492 return sortStep(step);
493 case 'reword':
494 return rewordStep(step, message);
495 }
496})();
497exports.Step = {
498 push: pushStep,
499 pop: popStep,
500 tag: tagStep,
501 edit: editStep,
502 sort: sortStep,
503 reword: rewordStep,
504 show: showStep,
505 assert: assertStep,
506 commit: commitStep,
507 current: getCurrentStep,
508 currentSuper: getCurrentSuperStep,
509 next: getNextStep,
510 nextSuper: getNextSuperStep,
511 base: getStepBase,
512 recentCommit: getRecentStepCommit,
513 recentSuperCommit: getRecentSuperStepCommit,
514 recentSubCommit: getRecentSubStepCommit,
515 descriptor: getStepDescriptor,
516 superDescriptor: getSuperStepDescriptor,
517 subDescriptor: getSubStepDescriptor,
518 initializeStepMap: initializeStepMap,
519 getStepMap: getStepMap,
520 ensureStepMap: ensureStepMap,
521 disposeStepMap: disposeStepMap,
522 updateStepMap: updateStepMap,
523};
524//# sourceMappingURL=step.js.map
\No newline at end of file