History

Update Git documentation (2.30.0)

Simon Legner committed 5 years ago

Commit 3600a9f1 parent 8f17cfa41 files · +4398 −1Browse files

Showing the changes to node_modules/simple-git/src/git.js (1 of 41 files). Show all

Unified · Split

node_modules/simple-git/src/git.js+1592−0
@@ -0,0 +1,1592 @@
1(function () {
2
3 'use strict';
4
5 var debug = require('debug')('simple-git');
6 var deferred = require('./util/deferred');
7 var exists = require('./util/exists');
8 var NOOP = function () {};
9 var responses = require('./responses');
10
11 /**
12 * Git handling for node. All public functions can be chained and all `then` handlers are optional.
13 *
14 * @param {string} baseDir base directory for all processes to run
15 *
16 * @param {Object} ChildProcess The ChildProcess module
17 * @param {Function} Buffer The Buffer implementation to use
18 *
19 * @constructor
20 */
21 function Git (baseDir, ChildProcess, Buffer) {
22 this._baseDir = baseDir;
23 this._runCache = [];
24
25 this.ChildProcess = ChildProcess;
26 this.Buffer = Buffer;
27 }
28
29 /**
30 * @type {string} The command to use to reference the git binary
31 */
32 Git.prototype._command = 'git';
33
34 /**
35 * @type {[key: string]: string} An object of key=value pairs to be passed as environment variables to the
36 * spawned child process.
37 */
38 Git.prototype._env = null;
39
40 /**
41 * @type {Function} An optional handler to use when a child process is created
42 */
43 Git.prototype._outputHandler = null;
44
45 /**
46 * @type {boolean} Property showing whether logging will be silenced - defaults to true in a production environment
47 */
48 Git.prototype._silentLogging = /prod/.test(process.env.NODE_ENV);
49
50 /**
51 * Sets the path to a custom git binary, should either be `git` when there is an installation of git available on
52 * the system path, or a fully qualified path to the executable.
53 *
54 * @param {string} command
55 * @returns {Git}
56 */
57 Git.prototype.customBinary = function (command) {
58 this._command = command;
59 return this;
60 };
61
62 /**
63 * Sets an environment variable for the spawned child process, either supply both a name and value as strings or
64 * a single object to entirely replace the current environment variables.
65 *
66 * @param {string|Object} name
67 * @param {string} [value]
68 * @returns {Git}
69 */
70 Git.prototype.env = function (name, value) {
71 if (arguments.length === 1 && typeof name === 'object') {
72 this._env = name;
73 }
74 else {
75 (this._env = this._env || {})[name] = value;
76 }
77
78 return this;
79 };
80
81 /**
82 * Sets the working directory of the subsequent commands.
83 *
84 * @param {string} workingDirectory
85 * @param {Function} [then]
86 * @returns {Git}
87 */
88 Git.prototype.cwd = function (workingDirectory, then) {
89 var git = this;
90 var next = Git.trailingFunctionArgument(arguments);
91
92 return this.exec(function () {
93 git._baseDir = workingDirectory;
94 if (!exists(workingDirectory, exists.FOLDER)) {
95 Git.exception(git, 'Git.cwd: cannot change to non-directory "' + workingDirectory + '"', next);
96 }
97 else {
98 next && next(null, workingDirectory);
99 }
100 });
101 };
102
103 /**
104 * Sets a handler function to be called whenever a new child process is created, the handler function will be called
105 * with the name of the command being run and the stdout & stderr streams used by the ChildProcess.
106 *
107 * @example
108 * require('simple-git')
109 * .outputHandler(function (command, stdout, stderr) {
110 * stdout.pipe(process.stdout);
111 * })
112 * .checkout('https://github.com/user/repo.git');
113 *
114 * @see https://nodejs.org/api/child_process.html#child_process_class_childprocess
115 * @see https://nodejs.org/api/stream.html#stream_class_stream_readable
116 * @param {Function} outputHandler
117 * @returns {Git}
118 */
119 Git.prototype.outputHandler = function (outputHandler) {
120 this._outputHandler = outputHandler;
121 return this;
122 };
123
124 /**
125 * Initialize a git repo
126 *
127 * @param {Boolean} [bare=false]
128 * @param {Function} [then]
129 */
130 Git.prototype.init = function (bare, then) {
131 var commands = ['init'];
132 var next = Git.trailingFunctionArgument(arguments);
133
134 if (bare === true) {
135 commands.push('--bare');
136 }
137
138 return this._run(commands, function (err) {
139 next && next(err);
140 });
141 };
142
143 /**
144 * Check the status of the local repo
145 *
146 * @param {Function} [then]
147 */
148 Git.prototype.status = function (then) {
149 return this._run(
150 ['status', '--porcelain', '-b', '-u'],
151 Git._responseHandler(then, 'StatusSummary')
152 );
153 };
154
155 /**
156 * List the stash(s) of the local repo
157 *
158 * @param {Object|Array} [options]
159 * @param {Function} [then]
160 */
161 Git.prototype.stashList = function (options, then) {
162 var handler = Git.trailingFunctionArgument(arguments);
163 var opt = (handler === then ? options : null) || {};
164
165 var splitter = opt.splitter || requireResponseHandler('ListLogSummary').SPLITTER;
166 var command = ["stash", "list", "--pretty=format:"
167 + requireResponseHandler('ListLogSummary').START_BOUNDARY
168 + "%H %ai %s%d %aN %ae".replace(/\s+/g, splitter)
169 + requireResponseHandler('ListLogSummary').COMMIT_BOUNDARY
170 ];
171
172 if (Array.isArray(opt)) {
173 command = command.concat(opt);
174 }
175
176 return this._run(command,
177 Git._responseHandler(handler, 'ListLogSummary', splitter)
178 );
179 };
180
181 /**
182 * Stash the local repo
183 *
184 * @param {Object|Array} [options]
185 * @param {Function} [then]
186 */
187 Git.prototype.stash = function (options, then) {
188 var command = ['stash'];
189 Git._appendOptions(command, Git.trailingOptionsArgument(arguments));
190 command.push.apply(command, Git.trailingArrayArgument(arguments));
191
192 return this._run(command, Git._responseHandler(Git.trailingFunctionArgument(arguments)));
193 };
194
195 /**
196 * Clone a git repo
197 *
198 * @param {string} repoPath
199 * @param {string} localPath
200 * @param {String[]} [options] Optional array of options to pass through to the clone command
201 * @param {Function} [then]
202 */
203 Git.prototype.clone = function (repoPath, localPath, options, then) {
204 var next = Git.trailingFunctionArgument(arguments);
205 var command = ['clone'].concat(Git.trailingArrayArgument(arguments));
206
207 for (var i = 0, iMax = arguments.length; i < iMax; i++) {
208 if (typeof arguments[i] === 'string') {
209 command.push(arguments[i]);
210 }
211 }
212
213 return this._run(command, function (err, data) {
214 next && next(err, data);
215 });
216 };
217
218 /**
219 * Mirror a git repo
220 *
221 * @param {string} repoPath
222 * @param {string} localPath
223 * @param {Function} [then]
224 */
225 Git.prototype.mirror = function (repoPath, localPath, then) {
226 return this.clone(repoPath, localPath, ['--mirror'], then);
227 };
228
229 /**
230 * Moves one or more files to a new destination.
231 *
232 * @see https://git-scm.com/docs/git-mv
233 *
234 * @param {string|string[]} from
235 * @param {string} to
236 * @param {Function} [then]
237 */
238 Git.prototype.mv = function (from, to, then) {
239 var handler = Git.trailingFunctionArgument(arguments);
240
241 var command = [].concat(from);
242 command.unshift('mv', '-v');
243 command.push(to);
244
245 this._run(command, Git._responseHandler(handler, 'MoveSummary'))
246 };
247
248 /**
249 * Internally uses pull and tags to get the list of tags then checks out the latest tag.
250 *
251 * @param {Function} [then]
252 */
253 Git.prototype.checkoutLatestTag = function (then) {
254 var git = this;
255 return this.pull(function () {
256 git.tags(function (err, tags) {
257 git.checkout(tags.latest, then);
258 });
259 });
260 };
261
262 /**
263 * Adds one or more files to source control
264 *
265 * @param {string|string[]} files
266 * @param {Function} [then]
267 */
268 Git.prototype.add = function (files, then) {
269 return this._run(['add'].concat(files), function (err, data) {
270 then && then(err);
271 });
272 };
273
274 /**
275 * Commits changes in the current working directory - when specific file paths are supplied, only changes on those
276 * files will be committed.
277 *
278 * @param {string|string[]} message
279 * @param {string|string[]} [files]
280 * @param {Object} [options]
281 * @param {Function} [then]
282 */
283 Git.prototype.commit = function (message, files, options, then) {
284 var handler = Git.trailingFunctionArgument(arguments);
285
286 var command = ['commit'];
287
288 [].concat(message).forEach(function (message) {
289 command.push('-m', message);
290 });
291
292 [].push.apply(command, [].concat(typeof files === "string" || Array.isArray(files) ? files : []));
293
294 Git._appendOptions(command, Git.trailingOptionsArgument(arguments));
295
296 return this._run(
297 command,
298 Git._responseHandler(handler, 'CommitSummary')
299 );
300 };
301
302 /**
303 * Gets a function to be used for logging.
304 *
305 * @param {string} level
306 * @param {string} [message]
307 *
308 * @returns {Function}
309 * @private
310 */
311 Git.prototype._getLog = function (level, message) {
312 var log = this._silentLogging ? NOOP : console[level].bind(console);
313 if (arguments.length > 1) {
314 log(message);
315 }
316 return log;
317 };
318
319 /**
320 * Pull the updated contents of the current repo
321 *
322 * @param {string} [remote] When supplied must also include the branch
323 * @param {string} [branch] When supplied must also include the remote
324 * @param {Object} [options] Optionally include set of options to merge into the command
325 * @param {Function} [then]
326 */
327 Git.prototype.pull = function (remote, branch, options, then) {
328 var command = ["pull"];
329 var handler = Git.trailingFunctionArgument(arguments);
330
331 if (typeof remote === 'string' && typeof branch === 'string') {
332 command.push(remote, branch);
333 }
334
335 Git._appendOptions(command, Git.trailingOptionsArgument(arguments));
336
337 return this._run(command, Git._responseHandler(handler, 'PullSummary'));
338 };
339
340 /**
341 * Fetch the updated contents of the current repo.
342 *
343 * @example
344 * .fetch('upstream', 'master') // fetches from master on remote named upstream
345 * .fetch(function () {}) // runs fetch against default remote and branch and calls function
346 *
347 * @param {string} [remote]
348 * @param {string} [branch]
349 * @param {Function} [then]
350 */
351 Git.prototype.fetch = function (remote, branch, then) {
352 var command = ["fetch"];
353 var next = Git.trailingFunctionArgument(arguments);
354 Git._appendOptions(command, Git.trailingOptionsArgument(arguments));
355
356 if (typeof remote === 'string' && typeof branch === 'string') {
357 command.push(remote, branch);
358 }
359
360 if (Array.isArray(remote)) {
361 command = command.concat(remote);
362 }
363
364 return this._run(
365 command,
366 Git._responseHandler(next, 'FetchSummary'),
367 {
368 concatStdErr: true
369 }
370 );
371 };
372
373 /**
374 * Disables/enables the use of the console for printing warnings and errors, by default messages are not shown in
375 * a production environment.
376 *
377 * @param {boolean} silence
378 * @returns {Git}
379 */
380 Git.prototype.silent = function (silence) {
381 this._silentLogging = !!silence;
382 return this;
383 };
384
385 /**
386 * List all tags. When using git 2.7.0 or above, include an options object with `"--sort": "property-name"` to
387 * sort the tags by that property instead of using the default semantic versioning sort.
388 *
389 * Note, supplying this option when it is not supported by your Git version will cause the operation to fail.
390 *
391 * @param {Object} [options]
392 * @param {Function} [then]
393 */
394 Git.prototype.tags = function (options, then) {
395 var next = Git.trailingFunctionArgument(arguments);
396
397 var command = ['-l'];
398 Git._appendOptions(command, Git.trailingOptionsArgument(arguments));
399
400 var hasCustomSort = command.some(function (option) {
401 return /^--sort=/.test(option);
402 });
403
404 return this.tag(
405 command,
406 Git._responseHandler(next, 'TagList', [hasCustomSort])
407 );
408 };
409
410 /**
411 * Rebases the current working copy. Options can be supplied either as an array of string parameters
412 * to be sent to the `git rebase` command, or a standard options object.
413 *
414 * @param {Object|String[]} [options]
415 * @param {Function} [then]
416 * @returns {Git}
417 */
418 Git.prototype.rebase = function (options, then) {
419 var command = ['rebase'];
420 Git._appendOptions(command, Git.trailingOptionsArgument(arguments));
421 command.push.apply(command, Git.trailingArrayArgument(arguments));
422
423
424 return this._run(command, Git._responseHandler(Git.trailingFunctionArgument(arguments)));
425 };
426
427 /**
428 * Reset a repo
429 *
430 * @param {string|string[]} [mode=soft] Either an array of arguments supported by the 'git reset' command, or the
431 * string value 'soft' or 'hard' to set the reset mode.
432 * @param {Function} [then]
433 */
434 Git.prototype.reset = function (mode, then) {
435 var command = ['reset'];
436 var next = Git.trailingFunctionArgument(arguments);
437 if (next === mode || typeof mode === 'string' || !mode) {
438 var modeStr = ['mixed', 'soft', 'hard'].includes(mode) ? mode : 'soft';
439 command.push('--' + modeStr);
440 }
441 else if (Array.isArray(mode)) {
442 command.push.apply(command, mode);
443 }
444
445 return this._run(command, function (err) {
446 next && next(err || null);
447 });
448 };
449
450 /**
451 * Revert one or more commits in the local working copy
452 *
453 * @param {string} commit The commit to revert. Can be any hash, offset (eg: `HEAD~2`) or range (eg: `master~5..master~2`)
454 * @param {Object} [options] Optional options object
455 * @param {Function} [then]
456 */
457 Git.prototype.revert = function (commit, options, then) {
458 var next = Git.trailingFunctionArgument(arguments);
459 var command = ['revert'];
460
461 Git._appendOptions(command, Git.trailingOptionsArgument(arguments));
462
463 if (typeof commit !== 'string') {
464 return this.exec(function () {
465 next && next(new TypeError("Commit must be a string"));
466 });
467 }
468
469 command.push(commit);
470 return this._run(command, function (err) {
471 next && next(err || null);
472 });
473 };
474
475 /**
476 * Add a lightweight tag to the head of the current branch
477 *
478 * @param {string} name
479 * @param {Function} [then]
480 */
481 Git.prototype.addTag = function (name, then) {
482 if (typeof name !== "string") {
483 return this.exec(function () {
484 then && then(new TypeError("Git.addTag requires a tag name"));
485 });
486 }
487
488 var command = [name];
489 return then ? this.tag(command, then) : this.tag(command);
490 };
491
492 /**
493 * Add an annotated tag to the head of the current branch
494 *
495 * @param {string} tagName
496 * @param {string} tagMessage
497 * @param {Function} [then]
498 */
499 Git.prototype.addAnnotatedTag = function (tagName, tagMessage, then) {
500 return this.tag(['-a', '-m', tagMessage, tagName], function (err) {
501 then && then(err);
502 });
503 };
504
505 /**
506 * Check out a tag or revision, any number of additional arguments can be passed to the `git checkout` command
507 * by supplying either a string or array of strings as the `what` parameter.
508 *
509 * @param {string|string[]} what One or more commands to pass to `git checkout`
510 * @param {Function} [then]
511 */
512 Git.prototype.checkout = function (what, then) {
513 var command = ['checkout'];
514 command = command.concat(what);
515
516 return this._run(command, function (err, data) {
517 then && then(err, !err && this._parseCheckout(data));
518 });
519 };
520
521 /**
522 * Check out a remote branch
523 *
524 * @param {string} branchName name of branch
525 * @param {string} startPoint (e.g origin/development)
526 * @param {Function} [then]
527 */
528 Git.prototype.checkoutBranch = function (branchName, startPoint, then) {
529 return this.checkout(['-b', branchName, startPoint], then);
530 };
531
532 /**
533 * Check out a local branch
534 *
535 * @param {string} branchName of branch
536 * @param {Function} [then]
537 */
538 Git.prototype.checkoutLocalBranch = function (branchName, then) {
539 return this.checkout(['-b', branchName], then);
540 };
541
542 /**
543 * Delete a local branch
544 *
545 * @param {string} branchName name of branch
546 * @param {Function} [then]
547 */
548 Git.prototype.deleteLocalBranch = function (branchName, then) {
549 return this.branch(['-d', branchName], then);
550 };
551
552 /**
553 * List all branches
554 *
555 * @param {Object | string[]} [options]
556 * @param {Function} [then]
557 */
558 Git.prototype.branch = function (options, then) {
559 var isDelete, responseHandler;
560 var next = Git.trailingFunctionArgument(arguments);
561 var command = ['branch'];
562
563 command.push.apply(command, Git.trailingArrayArgument(arguments));
564 Git._appendOptions(command, Git.trailingOptionsArgument(arguments));
565
566 if (!arguments.length || next === options) {
567 command.push('-a');
568 }
569
570 isDelete = ['-d', '-D', '--delete'].reduce(function (isDelete, flag) {
571 return isDelete || command.indexOf(flag) > 0;
572 }, false);
573
574 if (command.indexOf('-v') < 0) {
575 command.splice(1, 0, '-v');
576 }
577
578 responseHandler = isDelete
579 ? Git._responseHandler(next, 'BranchDeleteSummary', false)
580 : Git._responseHandler(next, 'BranchSummary');
581
582 return this._run(command, responseHandler);
583 };
584
585 /**
586 * Return list of local branches
587 *
588 * @param {Function} [then]
589 */
590 Git.prototype.branchLocal = function (then) {
591 return this.branch(['-v'], then);
592 };
593
594 /**
595 * Add config to local git instance
596 *
597 * @param {string} key configuration key (e.g user.name)
598 * @param {string} value for the given key (e.g your name)
599 * @param {Function} [then]
600 */
601 Git.prototype.addConfig = function (key, value, then) {
602 return this._run(['config', '--local', key, value], function (err, data) {
603 then && then(err, !err && data);
604 });
605 };
606
607 /**
608 * Executes any command against the git binary.
609 *
610 * @param {string[]|Object} commands
611 * @param {Function} [then]
612 *
613 * @returns {Git}
614 */
615 Git.prototype.raw = function (commands, then) {
616 var command = [];
617 if (Array.isArray(commands)) {
618 command = commands.slice(0);
619 }
620 else {
621 Git._appendOptions(command, Git.trailingOptionsArgument(arguments));
622 }
623
624 var next = Git.trailingFunctionArgument(arguments);
625
626 if (!command.length) {
627 return this.exec(function () {
628 next && next(new Error('Raw: must supply one or more command to execute'), null);
629 });
630 }
631
632 return this._run(command, function (err, data) {
633 next && next(err, !err && data || null);
634 });
635 };
636
637 /**
638 * Add a submodule
639 *
640 * @param {string} repo
641 * @param {string} path
642 * @param {Function} [then]
643 */
644 Git.prototype.submoduleAdd = function (repo, path, then) {
645 return this._run(['submodule', 'add', repo, path], function (err) {
646 then && then(err);
647 });
648 };
649
650 /**
651 * Update submodules
652 *
653 * @param {string[]} [args]
654 * @param {Function} [then]
655 */
656 Git.prototype.submoduleUpdate = function (args, then) {
657 if (typeof args === 'string') {
658 this._getLog('warn', 'Git#submoduleUpdate: args should be supplied as an array of individual arguments');
659 }
660
661 var next = Git.trailingFunctionArgument(arguments);
662 var command = (args !== next) ? args : [];
663
664 return this.subModule(['update'].concat(command), function (err, args) {
665 next && next(err, args);
666 });
667 };
668
669 /**
670 * Initialize submodules
671 *
672 * @param {string[]} [args]
673 * @param {Function} [then]
674 */
675 Git.prototype.submoduleInit = function (args, then) {
676 if (typeof args === 'string') {
677 this._getLog('warn', 'Git#submoduleInit: args should be supplied as an array of individual arguments');
678 }
679
680 var next = Git.trailingFunctionArgument(arguments);
681 var command = (args !== next) ? args : [];
682
683 return this.subModule(['init'].concat(command), function (err, args) {
684 next && next(err, args);
685 });
686 };
687
688 /**
689 * Call any `git submodule` function with arguments passed as an array of strings.
690 *
691 * @param {string[]} options
692 * @param {Function} [then]
693 */
694 Git.prototype.subModule = function (options, then) {
695 if (!Array.isArray(options)) {
696 return this.exec(function () {
697 then && then(new TypeError("Git.subModule requires an array of arguments"));
698 });
699 }
700
701 if (options[0] !== 'submodule') {
702 options.unshift('submodule');
703 }
704
705 return this._run(options, function (err, data) {
706 then && then(err || null, err ? null : data);
707 });
708 };
709
710 /**
711 * List remote
712 *
713 * @param {string[]} [args]
714 * @param {Function} [then]
715 */
716 Git.prototype.listRemote = function (args, then) {
717 var next = Git.trailingFunctionArgument(arguments);
718 var data = next === args || args === undefined ? [] : args;
719
720 if (typeof data === 'string') {
721 this._getLog('warn', 'Git#listRemote: args should be supplied as an array of individual arguments');
722 }
723
724 return this._run(['ls-remote'].concat(data), function (err, data) {
725 next && next(err, data);
726 });
727 };
728
729 /**
730 * Adds a remote to the list of remotes.
731 *
732 * @param {string} remoteName Name of the repository - eg "upstream"
733 * @param {string} remoteRepo Fully qualified SSH or HTTP(S) path to the remote repo
734 * @param {Function} [then]
735 * @returns {*}
736 */
737 Git.prototype.addRemote = function (remoteName, remoteRepo, then) {
738 return this._run(['remote', 'add', remoteName, remoteRepo], function (err) {
739 then && then(err);
740 });
741 };
742
743 /**
744 * Removes an entry from the list of remotes.
745 *
746 * @param {string} remoteName Name of the repository - eg "upstream"
747 * @param {Function} [then]
748 * @returns {*}
749 */
750 Git.prototype.removeRemote = function (remoteName, then) {
751 return this._run(['remote', 'remove', remoteName], function (err) {
752 then && then(err);
753 });
754 };
755
756 /**
757 * Gets the currently available remotes, setting the optional verbose argument to true includes additional
758 * detail on the remotes themselves.
759 *
760 * @param {boolean} [verbose=false]
761 * @param {Function} [then]
762 */
763 Git.prototype.getRemotes = function (verbose, then) {
764 var next = Git.trailingFunctionArgument(arguments);
765 var args = verbose === true ? ['-v'] : [];
766
767 return this.remote(args, function (err, data) {
768 next && next(err, !err && function () {
769 return data.trim().split('\n').filter(Boolean).reduce(function (remotes, remote) {
770 var detail = remote.trim().split(/\s+/);
771 var name = detail.shift();
772
773 if (!remotes[name]) {
774 remotes[name] = remotes[remotes.length] = {
775 name: name,
776 refs: {}
777 };
778 }
779
780 if (detail.length) {
781 remotes[name].refs[detail.pop().replace(/[^a-z]/g, '')] = detail.pop();
782 }
783
784 return remotes;
785 }, []).slice(0);
786 }());
787 });
788 };
789
790 /**
791 * Call any `git remote` function with arguments passed as an array of strings.
792 *
793 * @param {string[]} options
794 * @param {Function} [then]
795 */
796 Git.prototype.remote = function (options, then) {
797 if (!Array.isArray(options)) {
798 return this.exec(function () {
799 then && then(new TypeError("Git.remote requires an array of arguments"));
800 });
801 }
802
803 if (options[0] !== 'remote') {
804 options.unshift('remote');
805 }
806
807 return this._run(options, function (err, data) {
808 then && then(err || null, err ? null : data);
809 });
810 };
811
812 /**
813 * Merges from one branch to another, equivalent to running `git merge ${from} $[to}`, the `options` argument can
814 * either be an array of additional parameters to pass to the command or null / omitted to be ignored.
815 *
816 * @param {string} from
817 * @param {string} to
818 * @param {string[]} [options]
819 * @param {Function} [then]
820 */
821 Git.prototype.mergeFromTo = function (from, to, options, then) {
822 var commands = [
823 from,
824 to
825 ];
826 var callback = Git.trailingFunctionArgument(arguments);
827
828 if (Array.isArray(options)) {
829 commands = commands.concat(options);
830 }
831
832 return this.merge(commands, callback);
833 };
834
835 /**
836 * Runs a merge, `options` can be either an array of arguments
837 * supported by the [`git merge`](https://git-scm.com/docs/git-merge)
838 * or an options object.
839 *
840 * Conflicts during the merge result in an error response,
841 * the response type whether it was an error or success will be a MergeSummary instance.
842 * When successful, the MergeSummary has all detail from a the PullSummary
843 *
844 * @param {Object | string[]} [options]
845 * @param {Function} [then]
846 * @returns {*}
847 *
848 * @see ./responses/MergeSummary.js
849 * @see ./responses/PullSummary.js
850 */
851 Git.prototype.merge = function (options, then) {
852 var self = this;
853 var userHandler = Git.trailingFunctionArgument(arguments) || NOOP;
854 var mergeHandler = function (err, mergeSummary) {
855 if (!err && mergeSummary.failed) {
856 return Git.fail(self, mergeSummary, userHandler);
857 }
858
859 userHandler(err, mergeSummary);
860 };
861
862 var command = [];
863 Git._appendOptions(command, Git.trailingOptionsArgument(arguments));
864 command.push.apply(command, Git.trailingArrayArgument(arguments));
865
866 if (command[0] !== 'merge') {
867 command.unshift('merge');
868 }
869
870 if (command.length === 1) {
871 return this.exec(function () {
872 then && then(new TypeError("Git.merge requires at least one option"));
873 });
874 }
875
876 return this._run(command, Git._responseHandler(mergeHandler, 'MergeSummary'), {
877 concatStdErr: true
878 });
879 };
880
881 /**
882 * Call any `git tag` function with arguments passed as an array of strings.
883 *
884 * @param {string[]} options
885 * @param {Function} [then]
886 */
887 Git.prototype.tag = function (options, then) {
888 var command = [];
889 Git._appendOptions(command, Git.trailingOptionsArgument(arguments));
890 command.push.apply(command, Git.trailingArrayArgument(arguments));
891
892 if (command[0] !== 'tag') {
893 command.unshift('tag');
894 }
895
896 return this._run(command, Git._responseHandler(Git.trailingFunctionArgument(arguments)));
897 };
898
899 /**
900 * Updates repository server info
901 *
902 * @param {Function} [then]
903 */
904 Git.prototype.updateServerInfo = function (then) {
905 return this._run(["update-server-info"], function (err, data) {
906 then && then(err, !err && data);
907 });
908 };
909
910 /**
911 * Pushes the current committed changes to a remote, optionally specify the names of the remote and branch to use
912 * when pushing. Supply multiple options as an array of strings in the first argument - see examples below.
913 *
914 * @param {string|string[]} [remote]
915 * @param {string} [branch]
916 * @param {Function} [then]
917 */
918 Git.prototype.push = function (remote, branch, then) {
919 var command = [];
920 var handler = Git.trailingFunctionArgument(arguments);
921
922 if (typeof remote === 'string' && typeof branch === 'string') {
923 command.push(remote, branch);
924 }
925
926 if (Array.isArray(remote)) {
927 command = command.concat(remote);
928 }
929
930 Git._appendOptions(command, Git.trailingOptionsArgument(arguments));
931
932 if (command[0] !== 'push') {
933 command.unshift('push');
934 }
935
936 return this._run(command, function (err, data) {
937 handler && handler(err, !err && data);
938 });
939 };
940
941 /**
942 * Pushes the current tag changes to a remote which can be either a URL or named remote. When not specified uses the
943 * default configured remote spec.
944 *
945 * @param {string} [remote]
946 * @param {Function} [then]
947 */
948 Git.prototype.pushTags = function (remote, then) {
949 var command = ['push'];
950 if (typeof remote === "string") {
951 command.push(remote);
952 }
953 command.push('--tags');
954
955 then = typeof arguments[arguments.length - 1] === "function" ? arguments[arguments.length - 1] : null;
956
957 return this._run(command, function (err, data) {
958 then && then(err, !err && data);
959 });
960 };
961
962 /**
963 * Removes the named files from source control.
964 *
965 * @param {string|string[]} files
966 * @param {Function} [then]
967 */
968 Git.prototype.rm = function (files, then) {
969 return this._rm(files, '-f', then);
970 };
971
972 /**
973 * Removes the named files from source control but keeps them on disk rather than deleting them entirely. To
974 * completely remove the files, use `rm`.
975 *
976 * @param {string|string[]} files
977 * @param {Function} [then]
978 */
979 Git.prototype.rmKeepLocal = function (files, then) {
980 return this._rm(files, '--cached', then);
981 };
982
983 /**
984 * Returns a list of objects in a tree based on commit hash. Passing in an object hash returns the object's content,
985 * size, and type.
986 *
987 * Passing "-p" will instruct cat-file to determine the object type, and display its formatted contents.
988 *
989 * @param {string[]} [options]
990 * @param {Function} [then]
991 */
992 Git.prototype.catFile = function (options, then) {
993 return this._catFile('utf-8', arguments);
994 };
995
996 /**
997 * Equivalent to `catFile` but will return the native `Buffer` of content from the git command's stdout.
998 *
999 * @param {string[]} options
1000 * @param then
1001 */
1002 Git.prototype.binaryCatFile = function (options, then) {
1003 return this._catFile('buffer', arguments);
1004 };
1005
1006 Git.prototype._catFile = function (format, args) {
1007 var handler = Git.trailingFunctionArgument(args);
1008 var command = ['cat-file'];
1009 var options = args[0];
1010
1011 if (typeof options === 'string') {
1012 throw new TypeError('Git#catFile: options must be supplied as an array of strings');
1013 }
1014 else if (Array.isArray(options)) {
1015 command.push.apply(command, options);
1016 }
1017
1018 return this._run(command, function (err, data) {
1019 handler && handler(err, data);
1020 }, {
1021 format: format
1022 });
1023 };
1024
1025 /**
1026 * Return repository changes.
1027 *
1028 * @param {string[]} [options]
1029 * @param {Function} [then]
1030 */
1031 Git.prototype.diff = function (options, then) {
1032 var command = ['diff'];
1033
1034 if (typeof options === 'string') {
1035 command[0] += ' ' + options;
1036 this._getLog('warn',
1037 'Git#diff: supplying options as a single string is now deprecated, switch to an array of strings');
1038 }
1039 else if (Array.isArray(options)) {
1040 command.push.apply(command, options);
1041 }
1042
1043 if (typeof arguments[arguments.length - 1] === 'function') {
1044 then = arguments[arguments.length - 1];
1045 }
1046
1047 return this._run(command, function (err, data) {
1048 then && then(err, data);
1049 });
1050 };
1051
1052 Git.prototype.diffSummary = function (options, then) {
1053 var next = Git.trailingFunctionArgument(arguments);
1054 var command = ['--stat=4096'];
1055
1056 if (options && options !== next) {
1057 command.push.apply(command, [].concat(options));
1058 }
1059
1060 return this.diff(command, Git._responseHandler(next, 'DiffSummary'));
1061 };
1062
1063 /**
1064 * Wraps `git rev-parse`. Primarily used to convert friendly commit references (ie branch names) to SHA1 hashes.
1065 *
1066 * Options should be an array of string options compatible with the `git rev-parse`
1067 *
1068 * @param {string|string[]} [options]
1069 * @param {Function} [then]
1070 *
1071 * @see https://git-scm.com/docs/git-rev-parse
1072 */
1073 Git.prototype.revparse = function (options, then) {
1074 var command = ['rev-parse'];
1075
1076 if (typeof options === 'string') {
1077 command = command + ' ' + options;
1078 this._getLog('warn',
1079 'Git#revparse: supplying options as a single string is now deprecated, switch to an array of strings');
1080 }
1081 else if (Array.isArray(options)) {
1082 command.push.apply(command, options);
1083 }
1084
1085 if (typeof arguments[arguments.length - 1] === 'function') {
1086 then = arguments[arguments.length - 1];
1087 }
1088
1089 return this._run(command, function (err, data) {
1090 then && then(err, err ? null : String(data).trim());
1091 });
1092 };
1093
1094 /**
1095 * Show various types of objects, for example the file at a certain commit
1096 *
1097 * @param {string[]} [options]
1098 * @param {Function} [then]
1099 */
1100 Git.prototype.show = function (options, then) {
1101 var args = [].slice.call(arguments, 0);
1102 var handler = typeof args[args.length - 1] === "function" ? args.pop() : null;
1103 var command = ['show'];
1104 if (typeof options === 'string') {
1105 command = command + ' ' + options;
1106 this._getLog('warn',
1107 'Git#show: supplying options as a single string is now deprecated, switch to an array of strings');
1108 }
1109 else if (Array.isArray(options)) {
1110 command.push.apply(command, options);
1111 }
1112
1113 return this._run(command, function (err, data) {
1114 handler && handler(err, !err && data);
1115 });
1116 };
1117
1118 /**
1119 * @param {string} mode Required parameter "n" or "f"
1120 * @param {string[]} options
1121 * @param {Function} [then]
1122 */
1123 Git.prototype.clean = function (mode, options, then) {
1124 var handler = Git.trailingFunctionArgument(arguments);
1125
1126 if (typeof mode !== 'string' || !/[nf]/.test(mode)) {
1127 return this.exec(function () {
1128 handler && handler(new TypeError('Git clean mode parameter ("n" or "f") is required'));
1129 });
1130 }
1131
1132 if (/[^dfinqxX]/.test(mode)) {
1133 return this.exec(function () {
1134 handler && handler(new TypeError('Git clean unknown option found in ' + JSON.stringify(mode)));
1135 });
1136 }
1137
1138 var command = ['clean', '-' + mode];
1139 if (Array.isArray(options)) {
1140 command = command.concat(options);
1141 }
1142
1143 if (command.some(interactiveMode)) {
1144 return this.exec(function () {
1145 handler && handler(new TypeError('Git clean interactive mode is not supported'));
1146 });
1147 }
1148
1149 return this._run(command, function (err, data) {
1150 handler && handler(err, !err && data);
1151 });
1152
1153 function interactiveMode (option) {
1154 if (/^-[^\-]/.test(option)) {
1155 return option.indexOf('i') > 0;
1156 }
1157
1158 return option === '--interactive';
1159 }
1160 };
1161
1162 /**
1163 * Call a simple function at the next step in the chain.
1164 * @param {Function} [then]
1165 */
1166 Git.prototype.exec = function (then) {
1167 this._run([], function () {
1168 typeof then === 'function' && then();
1169 });
1170 return this;
1171 };
1172
1173 /**
1174 * Deprecated means of adding a regular function call at the next step in the chain. Use the replacement
1175 * Git#exec, the Git#then method will be removed in version 2.x
1176 *
1177 * @see exec
1178 * @deprecated
1179 */
1180 Git.prototype.then = function (then) {
1181 this._getLog(
1182 'error', `
1183Git#then is deprecated after version 1.72 and will be removed in version 2.x
1184To use promises switch to importing 'simple-git/promise'.`);
1185
1186 return this.exec(then);
1187 };
1188
1189 /**
1190 * Show commit logs from `HEAD` to the first commit.
1191 * If provided between `options.from` and `options.to` tags or branch.
1192 *
1193 * Additionally you can provide options.file, which is the path to a file in your repository. Then only this file will be considered.
1194 *
1195 * To use a custom splitter in the log format, set `options.splitter` to be the string the log should be split on.
1196 *
1197 * Options can also be supplied as a standard options object for adding custom properties supported by the git log command.
1198 * For any other set of options, supply options as an array of strings to be appended to the git log command.
1199 *
1200 * @param {Object|string[]} [options]
1201 * @param {string} [options.from] The first commit to include
1202 * @param {string} [options.to] The most recent commit to include
1203 * @param {string} [options.file] A single file to include in the result
1204 * @param {boolean} [options.multiLine] Optionally include multi-line commit messages
1205 *
1206 * @param {Function} [then]
1207 */
1208 Git.prototype.log = function (options, then) {
1209 var handler = Git.trailingFunctionArgument(arguments);
1210 var opt = (handler === then ? options : null) || {};
1211
1212 var splitter = opt.splitter || requireResponseHandler('ListLogSummary').SPLITTER;
1213 var format = opt.format || {
1214 hash: '%H',
1215 date: '%ai',
1216 message: '%s',
1217 refs: '%D',
1218 body: opt.multiLine ? '%B' : '%b',
1219 author_name: '%aN',
1220 author_email: '%ae'
1221 };
1222 var rangeOperator = (opt.symmetric !== false) ? '...' : '..';
1223
1224 var fields = Object.keys(format);
1225 var formatstr = fields.map(function (k) {
1226 return format[k];
1227 }).join(splitter);
1228 var suffix = [];
1229 var command = ["log", "--pretty=format:"
1230 + requireResponseHandler('ListLogSummary').START_BOUNDARY
1231 + formatstr
1232 + requireResponseHandler('ListLogSummary').COMMIT_BOUNDARY
1233 ];
1234
1235 if (Array.isArray(opt)) {
1236 command = command.concat(opt);
1237 opt = {};
1238 }
1239 else if (typeof arguments[0] === "string" || typeof arguments[1] === "string") {
1240 this._getLog('warn',
1241 'Git#log: supplying to or from as strings is now deprecated, switch to an options configuration object');
1242 opt = {
1243 from: arguments[0],
1244 to: arguments[1]
1245 };
1246 }
1247
1248 if (opt.n || opt['max-count']) {
1249 command.push("--max-count=" + (opt.n || opt['max-count']));
1250 }
1251
1252 if (opt.from && opt.to) {
1253 command.push(opt.from + rangeOperator + opt.to);
1254 }
1255
1256 if (opt.file) {
1257 suffix.push("--follow", options.file);
1258 }
1259
1260 'splitter n max-count file from to --pretty format symmetric multiLine'.split(' ').forEach(function (key) {
1261 delete opt[key];
1262 });
1263
1264 Git._appendOptions(command, opt);
1265
1266 return this._run(
1267 command.concat(suffix),
1268 Git._responseHandler(handler, 'ListLogSummary', [splitter, fields])
1269 );
1270 };
1271
1272 /**
1273 * Clears the queue of pending commands and returns the wrapper instance for chaining.
1274 *
1275 * @returns {Git}
1276 */
1277 Git.prototype.clearQueue = function () {
1278 this._runCache.length = 0;
1279 return this;
1280 };
1281
1282 /**
1283 * Check if a pathname or pathnames are excluded by .gitignore
1284 *
1285 * @param {string|string[]} pathnames
1286 * @param {Function} [then]
1287 */
1288 Git.prototype.checkIgnore = function (pathnames, then) {
1289 var handler = Git.trailingFunctionArgument(arguments);
1290 var command = ["check-ignore"];
1291
1292 if (handler !== pathnames) {
1293 command = command.concat(pathnames);
1294 }
1295
1296 return this._run(command, function (err, data) {
1297 handler && handler(err, !err && this._parseCheckIgnore(data));
1298 });
1299 };
1300
1301 /**
1302 * Validates that the current repo is a Git repo.
1303 *
1304 * @param {Function} [then]
1305 */
1306 Git.prototype.checkIsRepo = function (then) {
1307 function onError (exitCode, stdErr, done, fail) {
1308 if (exitCode === 128 && /(Not a git repository|Kein Git-Repository)/i.test(stdErr)) {
1309 return done(false);
1310 }
1311
1312 fail(stdErr);
1313 }
1314
1315 function handler (err, isRepo) {
1316 then && then(err, String(isRepo).trim() === 'true');
1317 }
1318
1319 return this._run(['rev-parse', '--is-inside-work-tree'], handler, {onError: onError});
1320 };
1321
1322 Git.prototype._rm = function (_files, options, then) {
1323 var files = [].concat(_files);
1324 var args = ['rm', options];
1325 args.push.apply(args, files);
1326
1327 return this._run(args, function (err) {
1328 then && then(err);
1329 });
1330 };
1331
1332 Git.prototype._parseCheckout = function (checkout) {
1333 // TODO
1334 };
1335
1336 /**
1337 * Parser for the `check-ignore` command - returns each
1338 * @param {string} [files]
1339 * @returns {string[]}
1340 */
1341 Git.prototype._parseCheckIgnore = function (files) {
1342 return files.split(/\n/g).filter(Boolean).map(function (file) {
1343 return file.trim()
1344 });
1345 };
1346
1347 /**
1348 * Schedules the supplied command to be run, the command should not include the name of the git binary and should
1349 * be an array of strings passed as the arguments to the git binary.
1350 *
1351 * @param {string[]} command
1352 * @param {Function} then
1353 * @param {Object} [opt]
1354 * @param {boolean} [opt.concatStdErr=false] Optionally concatenate stderr output into the stdout
1355 * @param {boolean} [opt.format="utf-8"] The format to use when reading the content of stdout
1356 * @param {Function} [opt.onError] Optional error handler for this command - can be used to allow non-clean exits
1357 * without killing the remaining stack of commands
1358 * @param {number} [opt.onError.exitCode]
1359 * @param {string} [opt.onError.stdErr]
1360 *
1361 * @returns {Git}
1362 */
1363 Git.prototype._run = function (command, then, opt) {
1364 if (typeof command === "string") {
1365 command = command.split(" ");
1366 }
1367 this._runCache.push([command, then, opt || {}]);
1368 this._schedule();
1369
1370 return this;
1371 };
1372
1373 Git.prototype._schedule = function () {
1374 if (!this._childProcess && this._runCache.length) {
1375 var git = this;
1376 var Buffer = git.Buffer;
1377 var task = git._runCache.shift();
1378
1379 var command = task[0];
1380 var then = task[1];
1381 var options = task[2];
1382
1383 debug(command);
1384
1385 var result = deferred();
1386
1387 var attempted = false;
1388 var attemptClose = function attemptClose (e) {
1389
1390 // closing when there is content, terminate immediately
1391 if (attempted || stdErr.length || stdOut.length) {
1392 result.resolve(e);
1393 attempted = true;
1394 }
1395
1396 // first attempt at closing but no content yet, wait briefly for the close/exit that may follow
1397 if (!attempted) {
1398 attempted = true;
1399 setTimeout(attemptClose.bind(this, e), 50);
1400 }
1401
1402 };
1403
1404 var stdOut = [];
1405 var stdErr = [];
1406 var spawned = git.ChildProcess.spawn(git._command, command.slice(0), {
1407 cwd: git._baseDir,
1408 env: git._env,
1409 windowsHide: true
1410 });
1411
1412 spawned.stdout.on('data', function (buffer) {
1413 stdOut.push(buffer);
1414 });
1415
1416 spawned.stderr.on('data', function (buffer) {
1417 stdErr.push(buffer);
1418 });
1419
1420 spawned.on('error', function (err) {
1421 stdErr.push(Buffer.from(err.stack, 'ascii'));
1422 });
1423
1424 spawned.on('close', attemptClose);
1425 spawned.on('exit', attemptClose);
1426
1427 result.promise.then(function (exitCode) {
1428 function done (output) {
1429 then.call(git, null, output);
1430 }
1431
1432 function fail (error) {
1433 Git.fail(git, error, then);
1434 }
1435
1436 delete git._childProcess;
1437
1438 if (exitCode && stdErr.length && options.onError) {
1439 options.onError(exitCode, Buffer.concat(stdErr).toString('utf-8'), done, fail);
1440 }
1441 else if (exitCode && stdErr.length) {
1442 fail(Buffer.concat(stdErr).toString('utf-8'));
1443 }
1444 else {
1445 if (options.concatStdErr) {
1446 [].push.apply(stdOut, stdErr);
1447 }
1448
1449 var stdOutput = Buffer.concat(stdOut);
1450 if (options.format !== 'buffer') {
1451 stdOutput = stdOutput.toString(options.format || 'utf-8');
1452 }
1453
1454 done(stdOutput);
1455 }
1456
1457 process.nextTick(git._schedule.bind(git));
1458 });
1459
1460 git._childProcess = spawned;
1461
1462 if (git._outputHandler) {
1463 git._outputHandler(command[0], git._childProcess.stdout, git._childProcess.stderr);
1464 }
1465 }
1466 };
1467
1468 /**
1469 * Handles an exception in the processing of a command.
1470 */
1471 Git.fail = function (git, error, handler) {
1472 git._getLog('error', error);
1473 git._runCache.length = 0;
1474 if (typeof handler === 'function') {
1475 handler.call(git, error, null);
1476 }
1477 };
1478
1479 /**
1480 * Given any number of arguments, returns the last argument if it is a function, otherwise returns null.
1481 * @returns {Function|null}
1482 */
1483 Git.trailingFunctionArgument = function (args) {
1484 var trailing = args[args.length - 1];
1485 return (typeof trailing === "function") ? trailing : null;
1486 };
1487
1488 /**
1489 * Given any number of arguments, returns the trailing options argument, ignoring a trailing function argument
1490 * if there is one. When not found, the return value is null.
1491 * @returns {Object|null}
1492 */
1493 Git.trailingOptionsArgument = function (args) {
1494 var options = args[(args.length - (Git.trailingFunctionArgument(args) ? 2 : 1))];
1495 return Object.prototype.toString.call(options) === '[object Object]' ? options : null;
1496 };
1497
1498 /**
1499 * Given any number of arguments, returns the trailing options array argument, ignoring a trailing function argument
1500 * if there is one. When not found, the return value is an empty array.
1501 * @returns {Array}
1502 */
1503 Git.trailingArrayArgument = function (args) {
1504 var options = args[(args.length - (Git.trailingFunctionArgument(args) ? 2 : 1))];
1505 return Object.prototype.toString.call(options) === '[object Array]' ? options : [];
1506 };
1507
1508 /**
1509 * Mutates the supplied command array by merging in properties in the options object. When the
1510 * value of the item in the options object is a string it will be concatenated to the key as
1511 * a single `name=value` item, otherwise just the name will be used.
1512 *
1513 * @param {string[]} command
1514 * @param {Object} options
1515 * @private
1516 */
1517 Git._appendOptions = function (command, options) {
1518 if (options === null) {
1519 return;
1520 }
1521
1522 Object.keys(options).forEach(function (key) {
1523 var value = options[key];
1524 if (typeof value === 'string') {
1525 command.push(key + '=' + value);
1526 }
1527 else {
1528 command.push(key);
1529 }
1530 });
1531 };
1532
1533 /**
1534 * Given the type of response and the callback to receive the parsed response,
1535 * uses the correct parser and calls back the callback.
1536 *
1537 * @param {Function} callback
1538 * @param {string} [type]
1539 * @param {Object[]} [args]
1540 *
1541 * @private
1542 */
1543 Git._responseHandler = function (callback, type, args) {
1544 return function (error, data) {
1545 if (typeof callback !== 'function') {
1546 return;
1547 }
1548
1549 if (error) {
1550 return callback(error, null);
1551 }
1552
1553 if (!type) {
1554 return callback(null, data);
1555 }
1556
1557 var handler = requireResponseHandler(type);
1558 var result = handler.parse.apply(handler, [data].concat(args === undefined ? [] : args));
1559
1560 callback(null, result);
1561 };
1562
1563 };
1564
1565 /**
1566 * Marks the git instance as having had a fatal exception by clearing the pending queue of tasks and
1567 * logging to the console.
1568 *
1569 * @param git
1570 * @param error
1571 * @param callback
1572 */
1573 Git.exception = function (git, error, callback) {
1574 git._runCache.length = 0;
1575 if (typeof callback === 'function') {
1576 callback(error instanceof Error ? error : new Error(error));
1577 }
1578
1579 git._getLog('error', error);
1580 };
1581
1582 module.exports = Git;
1583
1584 /**
1585 * Requires and returns a response handler based on its named type
1586 * @param {string} type
1587 */
1588 function requireResponseHandler (type) {
1589 return responses[type];
1590 }
1591
1592}());