avil13-co 2.2.5 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,34 +1,50 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.run = void 0;
3
4
  const ask_1 = require("./ask");
4
5
  const cli_commands_1 = require("./cli-commands");
5
6
  const commands_1 = require("./commands");
6
7
  const delete_barnch_1 = require("./commands/delete-barnch");
7
8
  const merge_branch_1 = require("./commands/merge-branch");
9
+ const terminal_1 = require("./terminal");
8
10
  const run = async () => {
9
- const userWish = (0, cli_commands_1.getWishOfUser)();
10
- if (cli_commands_1.Commands.Help === userWish) {
11
+ const { command, filter } = (0, cli_commands_1.parseArgs)();
12
+ if (command === cli_commands_1.Commands.Help) {
11
13
  console.log((0, cli_commands_1.getHelp)());
12
14
  return;
13
15
  }
14
- let branchType = commands_1.BranchesType.All;
15
- if (cli_commands_1.Commands.ShowMerged === userWish) {
16
- branchType = commands_1.BranchesType.Merged;
16
+ const branchType = command === cli_commands_1.Commands.ShowMerged ? commands_1.BranchesType.Merged
17
+ : command === cli_commands_1.Commands.ShowUnMerged ? commands_1.BranchesType.NotMerged : commands_1.BranchesType.All;
18
+ const deleting = command === cli_commands_1.Commands.DeleteBranch;
19
+ let branches = await (0, commands_1.getBranches)(branchType, deleting);
20
+ if (deleting) {
21
+ const currentBranch = await (0, commands_1.getCurrentBranchName)();
22
+ branches = branches.filter(branch => branch !== currentBranch);
23
+ if (!branches.length)
24
+ throw new Error('No local branches available for deletion.');
17
25
  }
18
- else if (cli_commands_1.Commands.ShowUnMerged === userWish) {
19
- branchType = commands_1.BranchesType.NotMerged;
26
+ (0, terminal_1.startTerminal)();
27
+ if (deleting) {
28
+ const selected = await (0, ask_1.askBranches)('Select branches to delete (Space to select)', branches, filter);
29
+ await (0, delete_barnch_1.deleteBranches)(selected);
20
30
  }
21
- const branches = await (0, commands_1.getBranches)(branchType);
22
- const { branch } = await (0, ask_1.ask)('Select branch', branches);
23
- if (cli_commands_1.Commands.DeleteBranch === userWish) {
24
- (0, delete_barnch_1.deleteBranch)(branch);
25
- return;
26
- }
27
- if (cli_commands_1.Commands.MergeBranch === userWish) {
28
- (0, merge_branch_1.mergeBranch)(branch);
29
- return;
31
+ else {
32
+ const { branch } = await (0, ask_1.ask)('Select branch', branches, filter);
33
+ if (command === cli_commands_1.Commands.MergeBranch)
34
+ await (0, merge_branch_1.mergeBranch)(branch);
35
+ else {
36
+ await (0, commands_1.checkoutBranch)(branch);
37
+ (0, terminal_1.print)(await (0, commands_1.getCurrentBranchName)());
38
+ }
30
39
  }
31
- await (0, commands_1.checkoutBranch)(branch);
32
- await (0, commands_1.getCurrentBranchName)();
33
40
  };
34
- run();
41
+ exports.run = run;
42
+ const removeInterruptHandler = (0, terminal_1.installInterruptHandler)();
43
+ (0, exports.run)().catch(error => {
44
+ (0, terminal_1.finishTerminal)();
45
+ console.error('Error: ' + (error instanceof Error ? error.message : String(error)));
46
+ process.exitCode = 1;
47
+ }).finally(() => {
48
+ (0, terminal_1.finishTerminal)();
49
+ removeInterruptHandler();
50
+ });
@@ -0,0 +1,2 @@
1
+ import { Answers, QuestionCollection } from 'inquirer';
2
+ export declare function prompt<T extends Answers = Answers>(questions: QuestionCollection<T>): Promise<T>;
package/dist/prompt.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.prompt = void 0;
7
+ const inquirer_1 = __importDefault(require("inquirer"));
8
+ const terminal_1 = require("./terminal");
9
+ function prompt(questions) {
10
+ const result = inquirer_1.default.prompt(questions);
11
+ // Inquirer 8 re-sends SIGINT asynchronously and may exit before Node handles
12
+ // it. Handle readline's raw-mode Ctrl+C synchronously instead.
13
+ const ui = result.ui;
14
+ if (ui) {
15
+ ui.rl.removeListener('SIGINT', ui.onForceClose);
16
+ ui.rl.once('SIGINT', () => {
17
+ ui.close();
18
+ (0, terminal_1.finishTerminal)(true);
19
+ process.exit(130);
20
+ });
21
+ }
22
+ return result;
23
+ }
24
+ exports.prompt = prompt;
@@ -0,0 +1,5 @@
1
+ /// <reference types="node" />
2
+ export declare function startTerminal(): void;
3
+ export declare function print(message: string): void;
4
+ export declare function finishTerminal(cancelled?: boolean): void;
5
+ export declare function installInterruptHandler(): () => NodeJS.Process;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.installInterruptHandler = exports.finishTerminal = exports.print = exports.startTerminal = void 0;
4
+ // Keep interactive output isolated from the user's shell history.
5
+ let active = false;
6
+ let messages = [];
7
+ function startTerminal() {
8
+ active = Boolean(process.stdout.isTTY && process.stdin.isTTY);
9
+ messages = [];
10
+ if (active)
11
+ process.stdout.write('\x1b[?1049h');
12
+ }
13
+ exports.startTerminal = startTerminal;
14
+ function print(message) {
15
+ if (!message.trim())
16
+ return;
17
+ if (active)
18
+ messages.push(message.trimEnd());
19
+ console.log(message.trimEnd());
20
+ }
21
+ exports.print = print;
22
+ function finishTerminal(cancelled = false) {
23
+ if (!active)
24
+ return;
25
+ active = false;
26
+ process.stdout.write('\x1b[?1049l\x1b[?25h');
27
+ if (!cancelled && messages.length)
28
+ console.log(messages.join('\n'));
29
+ messages = [];
30
+ }
31
+ exports.finishTerminal = finishTerminal;
32
+ function installInterruptHandler() {
33
+ const onInterrupt = () => {
34
+ finishTerminal(true);
35
+ process.exit(130);
36
+ };
37
+ process.on('SIGINT', onInterrupt);
38
+ return () => process.removeListener('SIGINT', onInterrupt);
39
+ }
40
+ exports.installInterruptHandler = installInterruptHandler;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "avil13-co",
3
- "version": "2.2.5",
3
+ "version": "2.3.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "public": true,
@@ -1,7 +1,22 @@
1
1
  import { expect, it } from 'vitest';
2
- import { sortOrder } from '../ask';
2
+ import { checkItem, sortOrder } from '../ask';
3
3
 
4
- it('should ', () => {
4
+ it.each(['[', '(', '*', '+', '?', '.', '\\', '$', '^'])('treats %s literally', symbol => {
5
+ expect(checkItem('task/' + symbol + '1234', symbol)).toBe(true);
6
+ expect(checkItem('task/1234', symbol)).toBe(false);
7
+ });
8
+
9
+ it('matches case-insensitive subsequences and ignores spaces', () => {
10
+ expect(checkItem('feature/TASK-1234', 't 14')).toBe(true);
11
+ expect(checkItem('feature/TASK-1234', '4321')).toBe(false);
12
+ });
13
+
14
+ it('returns zero for equivalent ranks', () => {
15
+ expect(sortOrder('task', 'first', 'second')).toBe(0);
16
+ expect(sortOrder('', 'first', 'second')).toBe(0);
17
+ });
18
+
19
+ it('ranks contiguous matches by position and keeps other choices stable', () => {
5
20
  const list = [
6
21
  'chore/EDODEV-10449-act-master',
7
22
  'chore/release-to-master',
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { Commands, parseArgs } from '../cli-commands';
3
+
4
+ describe('arguments', () => {
5
+ it('defaults to checkout', () => {
6
+ expect(parseArgs([])).toEqual({ command: Commands.Unknown, filter: '' });
7
+ });
8
+ it('preloads a checkout filter', () => {
9
+ expect(parseArgs(['-', '1234'])).toEqual({ command: Commands.Unknown, filter: '1234' });
10
+ });
11
+ it('accepts filters with commands', () => {
12
+ expect(parseArgs(['-D', '-', 'task', '1234'])).toEqual({
13
+ command: Commands.DeleteBranch, filter: 'task 1234',
14
+ });
15
+ });
16
+ it.each(['m', '-sm', '--show-merged'])('accepts merged alias %s', alias => {
17
+ expect(parseArgs([alias]).command).toBe(Commands.ShowMerged);
18
+ });
19
+ it.each(['un', '-un', '--no-merged'])('accepts unmerged alias %s', alias => {
20
+ expect(parseArgs([alias]).command).toBe(Commands.ShowUnMerged);
21
+ });
22
+ it('reports unknown options', () => {
23
+ expect(() => parseArgs(['--typo'])).toThrow('Unknown option');
24
+ });
25
+ });
@@ -0,0 +1,110 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { execFileSync } from 'child_process';
3
+ import { mkdtempSync, rmSync } from 'fs';
4
+ import { tmpdir } from 'os';
5
+ import { join } from 'path';
6
+ import inquirer from 'inquirer';
7
+ import { exec } from '../commands/exec';
8
+ import { BranchesType, getBranches } from '../commands/get-branches';
9
+ import { checkoutBranch } from '../commands/checkout-branch';
10
+ import { deleteBranches } from '../commands/delete-barnch';
11
+ import { mergeBranch } from '../commands/merge-branch';
12
+
13
+ const context = vi.hoisted(() => ({ cwd: '' }));
14
+ vi.mock('child_process', async importOriginal => {
15
+ const actual = await importOriginal<typeof import('child_process')>();
16
+ return {
17
+ ...actual,
18
+ execFile: (file, args, callback) => actual.execFile(file, args, { cwd: context.cwd }, callback),
19
+ };
20
+ });
21
+
22
+ function git(...args: string[]) {
23
+ return execFileSync('git', args, { cwd: context.cwd, encoding: 'utf8' }).trim();
24
+ }
25
+
26
+ beforeEach(() => {
27
+ context.cwd = mkdtempSync(join(tmpdir(), 'co-test-'));
28
+ git('init', '-q', '-b', 'main');
29
+ git('config', 'user.name', 'Test');
30
+ git('config', 'user.email', 'test@example.test');
31
+ git('commit', '-q', '--allow-empty', '-m', 'initial');
32
+ vi.spyOn(console, 'log').mockImplementation(() => {});
33
+ });
34
+
35
+ afterEach(() => {
36
+ vi.restoreAllMocks();
37
+ rmSync(context.cwd, { recursive: true, force: true });
38
+ });
39
+
40
+ describe('Git commands', () => {
41
+ it('reports Git diagnostics on failure', async () => {
42
+ await expect(exec(['checkout', 'missing', '--'])).rejects.toThrow('missing');
43
+ });
44
+
45
+ it('checks out branch names containing shell metacharacters literally', async () => {
46
+ const branch = 'task/1234;echo-pwned';
47
+ git('branch', branch);
48
+ await checkoutBranch(branch);
49
+ expect(git('branch', '--show-current')).toBe(branch);
50
+ });
51
+
52
+ it('preserves local origin-prefixed branches and deduplicates origin remote refs', async () => {
53
+ git('branch', 'origin/local');
54
+ git('update-ref', 'refs/remotes/origin/main', 'HEAD');
55
+ git('symbolic-ref', 'refs/remotes/origin/HEAD', 'refs/remotes/origin/main');
56
+ expect(await getBranches(BranchesType.All)).toEqual(['main', 'origin/local']);
57
+ });
58
+
59
+ it('excludes remote refs from deletion candidates', async () => {
60
+ git('update-ref', 'refs/remotes/origin/remote-only', 'HEAD');
61
+ expect(await getBranches(BranchesType.All, true)).toEqual(['main']);
62
+ });
63
+
64
+ it('deletes multiple selected branches after one confirmation', async () => {
65
+ git('branch', 'one');
66
+ git('branch', 'two');
67
+ const prompt = vi.spyOn(inquirer, 'prompt').mockResolvedValue({ answer: true });
68
+ await deleteBranches(['one', 'two']);
69
+ expect(git('branch', '--format=%(refname:short)')).toBe('main');
70
+ expect(prompt).toHaveBeenCalledTimes(1);
71
+ });
72
+
73
+ it('does not delete when confirmation is declined', async () => {
74
+ git('branch', 'one');
75
+ vi.spyOn(inquirer, 'prompt').mockResolvedValue({ answer: false });
76
+ await deleteBranches(['one']);
77
+ expect(git('branch', '--list', 'one')).toContain('one');
78
+ });
79
+
80
+ it('continues deleting and reports partial failures', async () => {
81
+ git('branch', 'one');
82
+ await expect(deleteBranches(['missing', 'one'], false)).rejects.toThrow('missing');
83
+ expect(git('branch', '--list', 'one')).toBe('');
84
+ });
85
+
86
+ it('offers safe deletion after successful merge, defaulting to yes', async () => {
87
+ git('branch', 'feature');
88
+ const prompt = vi.spyOn(inquirer, 'prompt')
89
+ .mockResolvedValueOnce({ answer: true })
90
+ .mockResolvedValueOnce({ remove: true });
91
+ await mergeBranch('feature');
92
+ expect(prompt.mock.calls[1][0]).toMatchObject({ default: true });
93
+ expect(git('branch', '--list', 'feature')).toBe('');
94
+ });
95
+
96
+ it('keeps the merged branch if deletion is declined', async () => {
97
+ git('branch', 'feature');
98
+ vi.spyOn(inquirer, 'prompt')
99
+ .mockResolvedValueOnce({ answer: true })
100
+ .mockResolvedValueOnce({ remove: false });
101
+ await mergeBranch('feature');
102
+ expect(git('branch', '--list', 'feature')).toContain('feature');
103
+ });
104
+
105
+ it('never offers deletion after a failed merge', async () => {
106
+ const prompt = vi.spyOn(inquirer, 'prompt').mockResolvedValue({ answer: true });
107
+ await expect(mergeBranch('missing')).rejects.toThrow('missing');
108
+ expect(prompt).toHaveBeenCalledTimes(1);
109
+ });
110
+ });
@@ -0,0 +1,56 @@
1
+ import { afterEach, beforeEach, expect, it, vi } from 'vitest';
2
+ import { finishTerminal, print, startTerminal } from '../terminal';
3
+
4
+ const descriptors = [process.stdin, process.stdout].map(stream =>
5
+ Object.getOwnPropertyDescriptor(stream, 'isTTY'));
6
+
7
+ function setTTY(value: boolean) {
8
+ for (const stream of [process.stdin, process.stdout]) {
9
+ Object.defineProperty(stream, 'isTTY', { value, configurable: true });
10
+ }
11
+ }
12
+
13
+ beforeEach(() => {
14
+ vi.spyOn(process.stdout, 'write').mockReturnValue(true);
15
+ vi.spyOn(console, 'log').mockImplementation(() => {});
16
+ });
17
+
18
+ afterEach(() => {
19
+ finishTerminal(true);
20
+ [process.stdin, process.stdout].forEach((stream, index) => {
21
+ const descriptor = descriptors[index];
22
+ if (descriptor) Object.defineProperty(stream, 'isTTY', descriptor);
23
+ else delete (stream as any).isTTY;
24
+ });
25
+ vi.restoreAllMocks();
26
+ });
27
+
28
+ it('does not emit screen controls outside a TTY', () => {
29
+ setTTY(false);
30
+ startTerminal();
31
+ print('result');
32
+ finishTerminal();
33
+ expect(process.stdout.write).not.toHaveBeenCalled();
34
+ expect(console.log).toHaveBeenCalledExactlyOnceWith('result');
35
+ });
36
+
37
+ it('restores the screen and replays results on success only once', () => {
38
+ setTTY(true);
39
+ startTerminal();
40
+ print('result');
41
+ finishTerminal();
42
+ finishTerminal();
43
+ expect(process.stdout.write).toHaveBeenNthCalledWith(1, '\x1b[?1049h');
44
+ expect(process.stdout.write).toHaveBeenNthCalledWith(2, '\x1b[?1049l\x1b[?25h');
45
+ expect(process.stdout.write).toHaveBeenCalledTimes(2);
46
+ expect(console.log).toHaveBeenCalledTimes(2);
47
+ });
48
+
49
+ it('restores the screen without replaying output on cancellation', () => {
50
+ setTTY(true);
51
+ startTerminal();
52
+ print('discard this');
53
+ finishTerminal(true);
54
+ expect(process.stdout.write).toHaveBeenLastCalledWith('\x1b[?1049l\x1b[?25h');
55
+ expect(console.log).toHaveBeenCalledTimes(1);
56
+ });
package/src/ask.ts CHANGED
@@ -1,62 +1,66 @@
1
1
  import inquirer from 'inquirer';
2
+ import { prompt } from './prompt';
2
3
 
3
- inquirer.registerPrompt(
4
- 'autocomplete',
5
- require('inquirer-autocomplete-prompt')
6
- );
4
+ const AutocompletePrompt = require('inquirer-autocomplete-prompt');
7
5
 
8
- const specSymbols = {
9
- _saveCursor: '\x1b7',
10
- _restoreCursor: '\x1b8',
11
- _clear: '\x1b[H\x1b[2J',
12
- };
6
+ class FilteredAutocompletePrompt extends AutocompletePrompt {
7
+ _run(callback) {
8
+ // `default` in the plugin selects a choice, it does not fill the input.
9
+ const filter = this.opt.initialFilter || '';
10
+ this.rl.line = filter;
11
+ this.rl.cursor = filter.length;
12
+ return super._run(callback);
13
+ }
14
+ }
13
15
 
14
- export const ask = async (message: string, choices: string[]) => {
15
- const prompt = inquirer.prompt({
16
- //@ts-ignore
16
+ inquirer.registerPrompt('autocomplete', FilteredAutocompletePrompt as any);
17
+
18
+ export const ask = async (message: string, choices: string[], initialFilter = '') => {
19
+ return prompt<{ branch: string }>({
17
20
  type: 'autocomplete',
18
21
  name: 'branch',
19
22
  message,
20
- source(answersSoFar, input = '') {
21
- let list = choices.filter(choiceItem => checkItem(choiceItem, input));
22
-
23
- list = list.sort((a, b) => {
24
- return sortOrder(input, a, b);
25
- });
26
-
27
- return Promise.resolve(list);
28
- }
29
- });
30
-
31
- return prompt;
23
+ initialFilter,
24
+ source(_answers, input = initialFilter) {
25
+ return Promise.resolve(choices
26
+ .filter(choice => checkItem(choice, input))
27
+ .sort((a, b) => sortOrder(input, a, b)));
28
+ },
29
+ } as any);
32
30
  };
33
31
 
34
- //
35
- export function checkItem(item: string, filterString: string): boolean {
36
- const reg = getRegExp(filterString);
37
-
38
- return reg.test(item);
32
+ export async function askBranches(message: string, choices: string[], filter = ''): Promise<string[]> {
33
+ const filtered = choices.filter(choice => checkItem(choice, filter));
34
+ if (!filtered.length) throw new Error('No branches match the filter.');
35
+ const { branches } = await prompt<{ branches: string[] }>({
36
+ type: 'checkbox',
37
+ name: 'branches',
38
+ message,
39
+ choices: filtered,
40
+ validate: (selected: string[]) => selected.length > 0 || 'Select at least one branch.',
41
+ });
42
+ return branches;
39
43
  }
40
44
 
41
- export function sortOrder(searchString: string, name1: string, name2: string): -1 | 1 {
42
- if (!name1.includes(searchString)) {
43
- return 1;
44
- }
45
- if (!name2.includes(searchString)) {
46
- return -1;
47
- }
48
-
49
- if ((name2.indexOf(searchString) < name1.indexOf(searchString))) {
50
- return 1;
45
+ export function checkItem(item: string, filterString: string): boolean {
46
+ // Literal, case-insensitive subsequence matching; no user-supplied regex.
47
+ const needle = filterString.replace(/\s/g, '').toLowerCase();
48
+ const haystack = item.toLowerCase();
49
+ let position = 0;
50
+ for (const character of needle) {
51
+ position = haystack.indexOf(character, position);
52
+ if (position === -1) return false;
53
+ position++;
51
54
  }
52
-
53
- return -1;
55
+ return true;
54
56
  }
55
57
 
56
-
57
- function getRegExp(filterString: string) {
58
- return new RegExp(
59
- filterString.split('').map(s => s.trim()).filter(Boolean).join('.*').replace('.*.*', '.*'),
60
- 'gi',
61
- );
58
+ export function sortOrder(searchString: string, name1: string, name2: string): number {
59
+ const search = searchString.toLowerCase();
60
+ const first = name1.toLowerCase().indexOf(search);
61
+ const second = name2.toLowerCase().indexOf(search);
62
+ if (first === second) return 0;
63
+ if (first === -1) return 1;
64
+ if (second === -1) return -1;
65
+ return first - second;
62
66
  }
@@ -1,7 +1,3 @@
1
- import { clc } from '../color';
2
-
3
- const args = process.argv.slice(2);
4
-
5
1
  export enum Commands {
6
2
  Unknown = 0,
7
3
  Help = 1,
@@ -9,52 +5,40 @@ export enum Commands {
9
5
  MergeBranch = 4,
10
6
  ShowMerged = 8,
11
7
  ShowUnMerged = 16,
12
- };
13
-
14
- export const getWishOfUser = (firstArg?: string): Commands => {
15
- const arg: string = firstArg || args[0];
16
-
17
- switch (true) {
18
- case ['help', '-h', '--h', '--help'].includes(arg):
19
- return Commands.Help;
20
-
21
- case ['-M', 'merge'].includes(arg):
22
- return Commands.MergeBranch;
23
-
24
- case ['-D', 'rm'].includes(arg):
25
- return Commands.DeleteBranch;
26
-
27
- case ['-sm', '--show-merged'].includes(arg):
28
- return Commands.ShowMerged;
8
+ }
29
9
 
30
- case ['-un', '--no-merged'].includes(arg):
31
- return Commands.ShowUnMerged;
10
+ export const getWishOfUser = (firstArg = process.argv[2]): Commands => {
11
+ if (['help', '-h', '--h', '--help'].includes(firstArg)) return Commands.Help;
12
+ if (['-M', 'merge'].includes(firstArg)) return Commands.MergeBranch;
13
+ if (['-D', 'rm'].includes(firstArg)) return Commands.DeleteBranch;
14
+ if (['m', '-sm', '--show-merged'].includes(firstArg)) return Commands.ShowMerged;
15
+ if (['un', '-un', '--no-merged'].includes(firstArg)) return Commands.ShowUnMerged;
16
+ return Commands.Unknown;
17
+ };
32
18
 
33
- default:
34
- return Commands.Unknown;
19
+ export function parseArgs(args = process.argv.slice(2)) {
20
+ const command = getWishOfUser(args[0] || '');
21
+ if (!args.length) return { command, filter: '' };
22
+ if (command === Commands.Unknown && args[0] !== '-') {
23
+ throw new Error('Unknown option: ' + args[0] + '. Use co --help.');
35
24
  }
25
+ const rest = args.slice(1);
26
+ if (rest[0] === '-') rest.shift();
27
+ return { command, filter: rest.join(' ') };
36
28
  }
37
29
 
38
-
39
- export const getHelp = (): string => {
40
- return `
30
+ export const getHelp = (): string => `
41
31
  DESCRIPTION:
42
- A console program for quickly switching between branches
43
-
44
- OPTIONS:
45
- help, -h, --h, --help
46
- Showing this message
47
-
48
- -M, merge
49
- Merge branch
50
-
51
- -sm, --show-merged
52
- Showing merged branches
53
-
54
- -un, --no-merged
55
- Showing not merged branches
56
-
57
- -D, rm
58
- Delete the selected branch
32
+ Quickly switch, merge and delete Git branches.
33
+
34
+ USAGE:
35
+ co Select a branch to check out
36
+ co - 1234 Start with an editable search filter
37
+ co -M [- filter] Merge, then offer to delete the source branch [Y/n]
38
+ co -D [- filter] Delete multiple local branches (Space to select)
39
+ co m, -sm, --show-merged Show merged branches
40
+ co un, -un, --no-merged Show unmerged branches
41
+ co help, -h, --h, --help Show this message
42
+
43
+ Ctrl+C cancels and restores the previous terminal screen.
59
44
  `;
60
- }
@@ -1,16 +1,8 @@
1
1
  import { exec } from './exec';
2
- import { clc } from '../color';
2
+ import { print } from '../terminal';
3
3
 
4
- //
5
4
  export async function checkoutBranch(branchName: string) {
6
- if (!branchName) {
7
- throw new Error('No branch...');
8
- }
9
-
10
- const checkoutCommand = `git checkout ${branchName}`;
11
-
12
- console.log(' ' + clc.green('git checkout', branchName) + '\n');
13
-
14
- const checkoutCommandResult = await exec(checkoutCommand);
15
- console.log(checkoutCommandResult);
5
+ if (!branchName) throw new Error('No branch selected.');
6
+ print('git checkout ' + branchName);
7
+ print(await exec(['checkout', branchName, '--']));
16
8
  }