1 | "use strict";
|
2 | var __assign = (this && this.__assign) || function () {
|
3 | __assign = Object.assign || function(t) {
|
4 | for (var s, i = 1, n = arguments.length; i < n; i++) {
|
5 | s = arguments[i];
|
6 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
7 | t[p] = s[p];
|
8 | }
|
9 | return t;
|
10 | };
|
11 | return __assign.apply(this, arguments);
|
12 | };
|
13 | Object.defineProperty(exports, "__esModule", { value: true });
|
14 | var Fs = require("fs-extra");
|
15 | var Tmp = require("tmp");
|
16 | var local_storage_1 = require("./local-storage");
|
17 | var paths_1 = require("./paths");
|
18 | var utils_1 = require("./utils");
|
19 |
|
20 |
|
21 |
|
22 | var exec = utils_1.Utils.exec;
|
23 |
|
24 | var conflict = /\n\s*<<<<<<< [^\n]+(\n(?:.|\n)+?)\n\s*=======(\n(?:.|\n)+?)\n\s*>>>>>>> [^\n]+/;
|
25 | function git(argv, options) {
|
26 | return gitBody(utils_1.Utils.git, argv, options);
|
27 | }
|
28 | var gitPrint = git.print = function (argv, options) {
|
29 | if (options === void 0) { options = {}; }
|
30 | return gitBody(utils_1.Utils.git.print, argv, options);
|
31 | };
|
32 |
|
33 |
|
34 |
|
35 | function pushTutorial(remote, baseBranch) {
|
36 | var relatedBranches = git(['branch', '-l', '-a']).split('\n').map(function (branch) {
|
37 | if (!branch) {
|
38 | return null;
|
39 | }
|
40 | branch = branch.split(/\*?\s+/)[1];
|
41 | var pathNodes = branch.split('/');
|
42 | if (pathNodes[0] === 'remotes') {
|
43 | if (pathNodes[1] !== remote) {
|
44 | return;
|
45 | }
|
46 | }
|
47 | var branchName = pathNodes.pop();
|
48 | if (branchName === baseBranch) {
|
49 | return branch;
|
50 | }
|
51 | if (branchName === baseBranch + "-history") {
|
52 | return branch;
|
53 | }
|
54 | if (branchName === baseBranch + "-root") {
|
55 | return branch;
|
56 | }
|
57 | if (new RegExp("^" + baseBranch + "-step\\d+$").test(branchName)) {
|
58 | return branch;
|
59 | }
|
60 | }).filter(Boolean);
|
61 | var relatedTags = git(['tag', '-l']).split('\n').map(function (tag) {
|
62 | if (!tag) {
|
63 | return null;
|
64 | }
|
65 | if (new RegExp("^" + baseBranch + "@(\\d+\\.\\d+\\.\\d+|next)$").test(tag)) {
|
66 | return tag;
|
67 | }
|
68 | if (new RegExp("^" + baseBranch + "@root@(\\d+\\.\\d+\\.\\d+|next)$").test(tag)) {
|
69 | return tag;
|
70 | }
|
71 | if (new RegExp("^" + baseBranch + "@step\\d+@(\\d+\\.\\d+\\.\\d+|next)$").test(tag)) {
|
72 | return tag;
|
73 | }
|
74 | }).filter(Boolean);
|
75 | var relatedBranchesNames = relatedBranches.map(function (b) { return b.split('/').pop(); });
|
76 | var deletedBranches = git(['ls-remote', '--heads', remote])
|
77 | .split('\n')
|
78 | .filter(Boolean)
|
79 | .map(function (line) { return line.split(/\s+/).pop(); })
|
80 | .filter(function (ref) { return !relatedBranchesNames.includes(ref.split('/').pop()); })
|
81 | .filter(function (ref) {
|
82 | var branch = ref.split('/').pop();
|
83 | return (branch === baseBranch ||
|
84 | branch === baseBranch + "-history" ||
|
85 | branch === baseBranch + "-root" ||
|
86 | new RegExp("^" + baseBranch + "-step\\d+$").test(branch));
|
87 | })
|
88 |
|
89 | .map(function (ref) { return ":" + ref; });
|
90 | var deletedTags = git(['ls-remote', '--tags', remote])
|
91 | .split('\n')
|
92 | .filter(Boolean)
|
93 | .map(function (line) { return line.split(/\s+/).pop(); })
|
94 | .filter(function (ref) { return !relatedTags.includes(ref.split('/').pop()); })
|
95 | .filter(function (ref) {
|
96 | return new RegExp("^refs/tags/" + baseBranch + "@(\\d+\\.\\d+\\.\\d+|next)$").test(ref) ||
|
97 | new RegExp("^refs/tags/" + baseBranch + "@root@(\\d+\\.\\d+\\.\\d+|next)$").test(ref) ||
|
98 | new RegExp("^refs/tags/" + baseBranch + "@step\\d+@(\\d+\\.\\d+\\.\\d+|next)$").test(ref);
|
99 | })
|
100 |
|
101 | .map(function (ref) { return ":" + ref; });
|
102 | var refs = relatedBranches.concat(relatedTags, deletedBranches, deletedTags);
|
103 | return gitPrint(['push', '-f', remote].concat(refs));
|
104 | }
|
105 |
|
106 |
|
107 | function pullTutorial(remote, baseBranch) {
|
108 | var relatedBranches = [];
|
109 | var relatedTags = [];
|
110 | git(['ls-remote', '--tags', '--heads', remote]).split('\n').forEach(function (line) {
|
111 | if (!line) {
|
112 | return;
|
113 | }
|
114 | var _a = line.split(/\s+/), ref = _a[1];
|
115 | if (new RegExp("^refs/tags/" + baseBranch + "@(root|step-\\d+@)?(\\d+\\.\\d+\\.\\d+|next)$").test(ref)) {
|
116 | relatedTags.push(ref.split('/').slice(2).join('/'));
|
117 | }
|
118 | if (new RegExp("^refs/heads/" + baseBranch + "(-root|-history|-step\\d+)?$").test(ref)) {
|
119 | relatedBranches.push(ref.split('/').slice(2).join('/'));
|
120 | }
|
121 | });
|
122 | var refs = relatedBranches.concat(relatedTags);
|
123 | var activeBranchName = exports.Git.activeBranchName();
|
124 | try {
|
125 | var sha1 = exports.Git(['rev-parse', activeBranchName]);
|
126 |
|
127 | exports.Git(['checkout', sha1]);
|
128 |
|
129 | gitPrint(['fetch', '--tags', '-f', remote].concat(refs));
|
130 |
|
131 | relatedBranches.forEach(function (branch) {
|
132 | try {
|
133 | exports.Git(['branch', '-D', branch]);
|
134 | }
|
135 | catch (e) {
|
136 |
|
137 | }
|
138 | exports.Git.print(['branch', '--track', branch, "remotes/" + remote + "/" + branch]);
|
139 | });
|
140 | }
|
141 | finally {
|
142 |
|
143 | exports.Git(['checkout', activeBranchName]);
|
144 | }
|
145 | }
|
146 |
|
147 | function getRefStep(ref) {
|
148 | if (ref === void 0) { ref = 'HEAD'; }
|
149 | if (getRootHash() === exports.Git(['rev-parse', ref])) {
|
150 | return 'root';
|
151 | }
|
152 | var match = exports.Git(['log', ref, '-1', '--format=%s']).match(/^Step (\d+(?:\.\d+)?)/);
|
153 | if (match) {
|
154 | return match[1];
|
155 | }
|
156 | return exports.Git(['rev-parse', '--short', ref]);
|
157 | }
|
158 |
|
159 | function tutorialStatus(options) {
|
160 | if (options === void 0) { options = {}; }
|
161 | exports.Git.print(['status']);
|
162 | var instructions;
|
163 | position: if (isRebasing()) {
|
164 | var head = exports.Git(['rev-parse', 'HEAD']);
|
165 | var rebaseHead = exports.Git(['rev-parse', 'REBASE_HEAD']);
|
166 | var headStep = getRefStep('HEAD');
|
167 | if (head === rebaseHead) {
|
168 | console.log("\nEditing " + headStep);
|
169 | instructions = 'edit';
|
170 | break position;
|
171 | }
|
172 | var rebaseHeadStep = getRefStep('REBASE_HEAD');
|
173 | var isConflict = local_storage_1.localStorage.getItem('REBASE_NEW_STEP') !== headStep;
|
174 | if (isConflict) {
|
175 | console.log("\nSolving conflict between " + headStep + " and " + rebaseHeadStep);
|
176 | instructions = 'conflict';
|
177 | break position;
|
178 | }
|
179 | console.log("\nBranched out from " + rebaseHeadStep + " to " + headStep);
|
180 | instructions = 'edit';
|
181 | }
|
182 | switch (options.instruct && instructions) {
|
183 | case 'edit':
|
184 | console.log('\n' + utils_1.freeText("\n To edit the current step, stage your changes and amend them:\n\n $ git add xxx\n $ git commit --amend\n\n Feel free to push or pop steps:\n\n $ tortilla step push/pop\n\n Once you finish, continue the rebase and Tortilla will take care of the rest:\n\n $ git rebase --continue\n\n You can go back to re-edit previous steps at any point, but be noted that this will discard all your changes thus far:\n\n $ tortilla step back\n\n If for some reason, at any point you decide to quit, use the comand:\n\n $ git rebase --abort\n "));
|
185 | case 'conflict':
|
186 | console.log('\n' + utils_1.freeText("\n Once you solved the conflict, stage your changes and continue the rebase.\n DO NOT amend your changes, push or pop steps:\n\n $ git add xxx\n $ git rebase --continue\n\n You can go back to re-edit previous steps at any point, but be noted that this will discard all your changes thus far:\n\n $ tortilla step back\n\n If for some reason, at any point you decide to quit, use the comand:\n\n $ git rebase --abort\n "));
|
187 | }
|
188 | }
|
189 |
|
190 |
|
191 | function gitBody(handler, argv, options) {
|
192 | options = __assign({ env: {} }, options);
|
193 |
|
194 | options.env = __assign({ GIT_DIR: null, GIT_WORK_TREE: null }, options.env);
|
195 | return handler(argv, options);
|
196 | }
|
197 |
|
198 | function isRebasing(path) {
|
199 | if (path === void 0) { path = null; }
|
200 | var paths = path ? paths_1.resolveProject(path).git : paths_1.Paths.git;
|
201 | return utils_1.Utils.exists(paths.rebaseMerge) || utils_1.Utils.exists(paths.rebaseApply);
|
202 | }
|
203 |
|
204 | function isCherryPicking() {
|
205 | return utils_1.Utils.exists(paths_1.Paths.git.heads.cherryPick) || utils_1.Utils.exists(paths_1.Paths.git.heads.revert);
|
206 | }
|
207 |
|
208 | function gonnaAmend() {
|
209 | return utils_1.Utils.childProcessOf('git', ['commit', '--amend']);
|
210 | }
|
211 |
|
212 | function tagExists(tag) {
|
213 | try {
|
214 | git(['rev-parse', tag]);
|
215 | return true;
|
216 | }
|
217 | catch (err) {
|
218 | return false;
|
219 | }
|
220 | }
|
221 |
|
222 |
|
223 | function getRecentCommit(offset, argv, options, path) {
|
224 | if (path === void 0) { path = null; }
|
225 | if (offset instanceof Array) {
|
226 | options = argv;
|
227 | argv = offset;
|
228 | offset = 0;
|
229 | }
|
230 | else {
|
231 | argv = argv || [];
|
232 | offset = offset || 0;
|
233 | }
|
234 | var hash = typeof offset === 'string' ? offset : ("HEAD~" + offset);
|
235 | argv = ['log', hash, '-1'].concat(argv);
|
236 | return git(argv, path ? __assign({}, options, { cwd: path }) : options);
|
237 | }
|
238 |
|
239 |
|
240 | function getStagedFiles(pattern) {
|
241 | var stagedFiles = git(['diff', '--name-only', '--cached'])
|
242 | .split('\n')
|
243 | .filter(Boolean);
|
244 | return utils_1.Utils.filterMatches(stagedFiles, pattern);
|
245 | }
|
246 |
|
247 | function getActiveBranchName(path) {
|
248 | if (path === void 0) { path = null; }
|
249 | if (!isRebasing(path)) {
|
250 | return git(['rev-parse', '--abbrev-ref', 'HEAD'], path ? { cwd: path } : null);
|
251 | }
|
252 |
|
253 | var branchHash = git(['reflog', '--format=%gd %gs'], path ? { cwd: path } : null)
|
254 | .split('\n')
|
255 | .filter(Boolean)
|
256 | .map(function (line) { return line.split(' '); })
|
257 | .map(function (split) { return [split.shift(), split.join(' ')]; })
|
258 | .find(function (_a) {
|
259 | var ref = _a[0], msg = _a[1];
|
260 | return msg.match(/^rebase -i \(start\)/);
|
261 | })
|
262 | .shift()
|
263 | .match(/^HEAD@\{(\d+)\}$/)
|
264 | .slice(1)
|
265 | .map(function (i) { return "HEAD@{" + ++i + "}"; })
|
266 | .map(function (ref) { return git(['rev-parse', ref]); })
|
267 | .pop();
|
268 |
|
269 | return Fs.readdirSync(paths_1.Paths.git.refs.heads).find(function (branchName) {
|
270 | return git(['rev-parse', branchName], path ? { cwd: path } : null) === branchHash;
|
271 | });
|
272 | }
|
273 |
|
274 | function getRootHash(head, options) {
|
275 | if (head === void 0) { head = 'HEAD'; }
|
276 | if (options === void 0) { options = {}; }
|
277 | return git(['rev-list', '--max-parents=0', head], options);
|
278 | }
|
279 | function getRoot() {
|
280 | try {
|
281 | return git(['rev-parse', '--show-toplevel']);
|
282 |
|
283 | }
|
284 | catch (e) {
|
285 | return '';
|
286 | }
|
287 | }
|
288 | function edit(initialContent) {
|
289 | var editor = getEditor();
|
290 | var file = Tmp.fileSync();
|
291 | Fs.writeFileSync(file.name, initialContent);
|
292 | exec.print('sh', ['-c', editor + " " + file.name]);
|
293 | var content = Fs.readFileSync(file.name).toString();
|
294 | file.removeCallback();
|
295 | return content;
|
296 | }
|
297 |
|
298 | function getEditor() {
|
299 | var editor = process.env.GIT_EDITOR;
|
300 | if (!editor) {
|
301 | try {
|
302 | editor = git(['config', 'core.editor']);
|
303 | }
|
304 | catch (e) {
|
305 |
|
306 | }
|
307 | }
|
308 | if (!editor) {
|
309 | try {
|
310 | editor = git(['var', 'GIT_EDITOR']);
|
311 | }
|
312 | catch (e) {
|
313 |
|
314 | }
|
315 | }
|
316 | if (!editor) {
|
317 | throw Error('Git editor could not be found');
|
318 | }
|
319 | return editor;
|
320 | }
|
321 |
|
322 |
|
323 | function normalizeArgv(argv) {
|
324 | argv = argv.slice();
|
325 | {
|
326 | var i = argv.indexOf('--format');
|
327 | if (i !== -1) {
|
328 | argv.splice(i, 2, "--format=" + argv[i + 1]);
|
329 | }
|
330 | }
|
331 | return argv;
|
332 | }
|
333 | function getRevisionIdFromObject(object) {
|
334 | return git(['rev-list', '-n', '1', object]);
|
335 | }
|
336 | exports.Git = utils_1.Utils.extend(git.bind(null), git, {
|
337 | pushTutorial: pushTutorial,
|
338 | pullTutorial: pullTutorial,
|
339 | tutorialStatus: tutorialStatus,
|
340 | conflict: conflict,
|
341 | rebasing: isRebasing,
|
342 | cherryPicking: isCherryPicking,
|
343 | gonnaAmend: gonnaAmend,
|
344 | tagExists: tagExists,
|
345 | recentCommit: getRecentCommit,
|
346 | stagedFiles: getStagedFiles,
|
347 | activeBranchName: getActiveBranchName,
|
348 | rootHash: getRootHash,
|
349 | root: getRoot,
|
350 | edit: edit,
|
351 | editor: getEditor,
|
352 | normalizeArgv: normalizeArgv,
|
353 | getRevisionIdFromObject: getRevisionIdFromObject,
|
354 | });
|
355 |
|
\ | No newline at end of file |