git-push-time 1.1.2 → 1.1.4

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/bin/index.js CHANGED
@@ -1,339 +1,321 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from 'commander';
4
- import chalk from 'chalk';
5
- import fs from 'fs';
6
- import path from 'path';
7
- import { fileURLToPath } from 'url';
8
- import { checkGitInstalled, getCurrentBranch, getDashboardGitStatus } from '../lib/git.js';
9
- import { getCurrentGitHubUser } from '../lib/github.js';
10
- import { runPushWizard } from '../lib/commands/push.js';
11
- import { runPullCommand } from '../lib/commands/pull.js';
12
- import { runEditCommitTimeCommand } from '../lib/commands/edit-time.js';
13
- import { runFixCommand } from '../lib/commands/fix.js';
14
- import {
15
- runGitHubMenu,
16
- promptCreateRepo,
17
- showUserRepos,
18
- } from '../lib/commands/github.js';
19
- import { promptContinueOrBack, selectBox } from '../lib/prompt-helpers.js';
20
- import {
21
- printBanner,
22
- printDashboardStatus,
23
- printGitMissing,
24
- printCancelled,
25
- styleCliHelp
26
- } from '../lib/ui.js';
27
-
28
- const __filename = fileURLToPath(import.meta.url);
29
- const __dirname = path.dirname(__filename);
30
-
31
- function getPackageVersion() {
32
- try {
33
- const raw = fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8');
34
- return JSON.parse(raw).version || '1.1.0';
35
- } catch {
36
- return '1.1.0';
37
- }
38
- }
39
-
40
- const VERSION = getPackageVersion();
41
-
42
- function isExitPromptError(err) {
43
- const msg = String(err?.message || '');
44
- return (
45
- err?.name === 'ExitPromptError' ||
46
- msg.includes('User force closed the prompt') ||
47
- msg.includes('SIGINT')
48
- );
49
- }
50
-
51
- /**
52
- * Kiểm tra xem lệnh có được gọi "trần" (không flag/subcommand nào) hay không.
53
- */
54
- function isBareInvocation(options = {}) {
55
- return (
56
- !options.repo &&
57
- !options.message &&
58
- !options.date &&
59
- !options.branch &&
60
- !options.dir &&
61
- !options.force &&
62
- !options.fix
63
- );
64
- }
65
-
66
- /**
67
- * Menu chính khi chạy `git-push-time` không truyền flag
68
- */
69
- async function promptMainMenu() {
70
- return await selectBox({
71
- message: 'Bảng điều khiển tác vụ chính',
72
- hint: '↑↓ di chuyển · Enter chọn thao tác · Esc thoát',
73
- pageSize: 10,
74
- choices: [
75
- {
76
- name: `${chalk.hex('#38BDF8').bold('→ PUSH ')} ${chalk.hex('#334155')('│')} ${chalk.hex('#F1F5F9')('Đẩy code lên Git với giờ tùy chỉnh')}`,
77
- value: 'push',
78
- description: 'Đẩy code lên Git với tùy chỉnh thời gian commit, kiểm tra secret và branch',
79
- },
80
- {
81
- name: `${chalk.hex('#A78BFA').bold('→ GITHUB ')} ${chalk.hex('#334155')('│')} ${chalk.hex('#CBD5E1')('Quản lý tài khoản & tạo repository')}`,
82
- value: 'github',
83
- description: 'Tạo nhanh repo GitHub public/private, liên kết remote tự động',
84
- },
85
- {
86
- name: `${chalk.hex('#34D399').bold('→ PULL ')} ${chalk.hex('#334155')('│')} ${chalk.hex('#CBD5E1')('Cập nhật code mới nhất từ remote')}`,
87
- value: 'pull',
88
- description: 'Pull an toàn với fast-forward hoặc rebase, tránh conflict',
89
- },
90
- {
91
- name: `${chalk.hex('#FBBF24').bold('→ EDIT-TIME ')} ${chalk.hex('#334155')('│')} ${chalk.hex('#CBD5E1')('Đổi timestamp commit lịch sử bất kỳ')}`,
92
- value: 'edit-time',
93
- description: 'Viết lại ngày giờ cho các commit đã tạo (Backdate commit)',
94
- },
95
- {
96
- name: `${chalk.hex('#F43F5E').bold('→ FIX ')} ${chalk.hex('#334155')('│')} ${chalk.hex('#CBD5E1')('Quét & gỡ key bí mật, bảo vệ GH013')}`,
97
- value: 'fix',
98
- description: 'Quét file .env/secrets, gỡ khỏi commit và cập nhật .gitignore',
99
- },
100
- {
101
- name: `${chalk.hex('#64748B').bold('x THOÁT ')} ${chalk.hex('#334155')('│')} ${chalk.hex('#64748B')('Đóng chương trình')}`,
102
- value: 'exit',
103
- },
104
- ],
105
- });
106
- }
107
-
108
- /**
109
- * Vòng lặp Menu chính (không đệ quy — callback back chỉ trả sentinel)
110
- */
111
- async function runMainMenuLoop(initialOptions = {}) {
112
- while (true) {
113
- const cwd = process.cwd();
114
- let branch = 'main';
115
- try {
116
- branch = getCurrentBranch(cwd);
117
- } catch {}
118
- let ghStatus = { authenticated: false };
119
- try {
120
- ghStatus = await getCurrentGitHubUser();
121
- } catch {
122
- ghStatus = { authenticated: false };
123
- }
124
-
125
- const gitStatus = getDashboardGitStatus(cwd);
126
- printDashboardStatus({
127
- targetDir: cwd,
128
- currentBranch: branch,
129
- githubUser: ghStatus.authenticated ? ghStatus.user : null,
130
- remoteUrl: gitStatus.remoteUrl,
131
- changes: gitStatus.changes,
132
- lastCommit: gitStatus.lastCommit,
133
- version: VERSION,
134
- });
135
- let action;
136
- try {
137
- action = await promptMainMenu();
138
- } catch (err) {
139
- if (isExitPromptError(err)) {
140
- printCancelled();
141
- process.exit(0);
142
- }
143
- throw err;
144
- }
145
-
146
- if (action === 'exit') {
147
- printCancelled();
148
- process.exit(0);
149
- }
150
-
151
- try {
152
- if (action === 'push') {
153
- const res = await runPushWizard(initialOptions, async () => 'back');
154
- // 'back' = user muốn về menu → tiếp tục loop; ngược lại push xong thì thoát
155
- if (res === 'back') continue;
156
- return;
157
- }
158
-
159
- if (action === 'github') {
160
- const ghResult = await runGitHubMenu({ defaultDir: cwd });
161
- if (ghResult && ghResult.autoPush && ghResult.repoUrl) {
162
- // Sau khi tạo repo mới, chuyển tiếp thẳng vào luồng push
163
- const res = await runPushWizard({ ...initialOptions, repo: ghResult.repoUrl }, async () => 'back');
164
- if (res === 'back') continue;
165
- return;
166
- }
167
- } else if (action === 'pull') {
168
- await runPullCommand('origin', undefined, { fromMenu: true });
169
- } else if (action === 'edit-time') {
170
- await runEditCommitTimeCommand({ fromMenu: true });
171
- } else if (action === 'fix') {
172
- await runFixCommand({ fromMenu: true });
173
- }
174
- } catch (err) {
175
- if (isExitPromptError(err)) {
176
- printCancelled();
177
- process.exit(0);
178
- }
179
- throw err;
180
- }
181
-
182
- let next;
183
- try {
184
- next = await promptContinueOrBack('Hoàn tất! Bạn muốn tiếp tục hay quay lại bảng điều khiển?');
185
- } catch (err) {
186
- if (isExitPromptError(err)) {
187
- printCancelled();
188
- process.exit(0);
189
- }
190
- throw err;
191
- }
192
- if (next === 'exit') {
193
- process.exit(0);
194
- }
195
- }
196
- }
197
-
198
- async function main(options = {}) {
199
- printBanner(VERSION);
200
-
201
- if (!checkGitInstalled()) {
202
- printGitMissing();
203
- process.exit(1);
204
- }
205
-
206
- const isInteractive = !options.yes;
207
-
208
- if (isInteractive && isBareInvocation(options)) {
209
- try {
210
- await runMainMenuLoop(options);
211
- } catch (err) {
212
- if (isExitPromptError(err)) {
213
- printCancelled();
214
- process.exit(0);
215
- }
216
- throw err;
217
- }
218
- return;
219
- }
220
-
221
- // Chạy trực tiếp push wizard với các options được cung cấp
222
- try {
223
- await runPushWizard(options);
224
- } catch (err) {
225
- if (isExitPromptError(err)) {
226
- printCancelled();
227
- process.exit(0);
228
- }
229
- throw err;
230
- }
231
- }
232
-
233
- /* ==========================================================================
234
- COMMANDER CLI CONFIGURATION
235
- ========================================================================== */
236
-
237
- const program = new Command();
238
- styleCliHelp(program);
239
-
240
- program
241
- .name('git-push-time')
242
- .description('Bộ công cụ Git & GitHub CLI: Đẩy code siêu tốc, kết nối GitHub tạo repo, chỉnh thời gian commit và bảo vệ secret')
243
- .version(VERSION)
244
- .option('-r, --repo <url>', 'Đường dẫn Git repository (URL hoặc để trống để chọn/tạo mới)')
245
- .option('-d, --date <datetime>', 'Thời gian commit (VD: "2024-01-15 14:30:00", "-2d", "-5h", "now")')
246
- .option('-m, --message <msg>', 'Thông điệp commit (Commit message)')
247
- .option('-b, --branch <branch>', 'Tên branch trên remote (Mặc định: main)')
248
- .option('-C, --dir <path>', 'Thư mục nguồn cần push (Mặc định: thư mục hiện tại)')
249
- .option('-f, --force', 'Thực hiện push đè (--force)', false)
250
- .option('-y, --yes', 'Bỏ qua giao diện hỏi, tự động dùng tham số hoặc giá trị mặc định', false)
251
- .option('--fix', 'Tự động gỡ file secret khỏi stage & thêm .gitignore (an toàn, không xóa file thật)', false)
252
- .action(async (opts) => {
253
- await main(opts);
254
- });
255
-
256
- // LỆNH GITHUB
257
- const ghCmd = program
258
- .command('github')
259
- .description('Quản lý tài khoản GitHub và tạo repository mới trực tiếp')
260
- .action(async () => {
261
- printBanner(VERSION);
262
- await runGitHubMenu();
263
- });
264
-
265
- ghCmd
266
- .command('create')
267
- .description('Tạo repository mới trên GitHub (Public hoặc Private)')
268
- .option('-C, --dir <path>', 'Thư mục nguồn cần liên kết')
269
- .action(async (opts) => {
270
- printBanner(VERSION);
271
- const res = await promptCreateRepo({ targetDir: opts.dir || process.cwd() });
272
- if (res.ok && res.repo) {
273
- console.log(`\n » Repo URL: ${res.repo.cloneUrl}\n`);
274
- }
275
- });
276
-
277
- ghCmd
278
- .command('whoami')
279
- .description('Xem thông tin tài khoản GitHub đang kết nối')
280
- .action(async () => {
281
- printBanner(VERSION);
282
- await showGitHubStatus();
283
- });
284
-
285
- ghCmd
286
- .command('repos')
287
- .description('Liệt kê danh sách repository trên GitHub của bạn')
288
- .action(async () => {
289
- printBanner(VERSION);
290
- await showUserRepos();
291
- });
292
-
293
- ghCmd
294
- .command('login')
295
- .description('Đăng nhập tài khoản GitHub bằng OAuth Device Flow hoặc Token')
296
- .action(async () => {
297
- printBanner(VERSION);
298
- await promptGitHubLogin();
299
- });
300
-
301
- ghCmd
302
- .command('logout')
303
- .description('Đăng xuất tài khoản GitHub khỏi máy')
304
- .action(async () => {
305
- printBanner(VERSION);
306
- logoutGitHub();
307
- });
308
-
309
- // LỆNH FIX
310
- program
311
- .command('fix')
312
- .description('Quét & tự động gỡ file secret khỏi stage, cập nhật .gitignore và làm sạch commit unpushed')
313
- .option('-C, --dir <path>', 'Thư mục cần quét fix (Mặc định: thư mục hiện tại)')
314
- .action(async (opts) => {
315
- printBanner(VERSION);
316
- await runFixCommand(opts);
317
- });
318
-
319
- // LỆNH PULL
320
- program
321
- .command('pull [remote] [branch]')
322
- .description('Cập nhật code từ remote bằng fast-forward --ff-only hoặc --rebase')
323
- .option('-C, --dir <path>', 'Thư mục repo cần pull (Mặc định: thư mục hiện tại)')
324
- .action(async (remote, branch, opts) => {
325
- printBanner(VERSION);
326
- await runPullCommand(remote || 'origin', branch, opts || {});
327
- });
328
-
329
- // LỆNH EDIT-TIME
330
- program
331
- .command('edit-time')
332
- .description('Sửa thời gian của commit lịch sử (Backdate commit hash)')
333
- .option('-C, --dir <path>', 'Thư mục repo (Mặc định: thư mục hiện tại)')
334
- .action(async (opts) => {
335
- printBanner(VERSION);
336
- await runEditCommitTimeCommand(opts);
337
- });
338
-
339
- program.parse(process.argv);
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import chalk from 'chalk';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+ import { checkGitInstalled, getCurrentBranch, getDashboardGitStatus } from '../lib/git.js';
9
+ import { getCurrentGitHubUser } from '../lib/github.js';
10
+ import { runPushWizard } from '../lib/commands/push.js';
11
+ import { runPullCommand } from '../lib/commands/pull.js';
12
+ import { runEditCommitTimeCommand } from '../lib/commands/edit-time.js';
13
+ import { runFixCommand } from '../lib/commands/fix.js';
14
+ import {
15
+ runGitHubMenu,
16
+ promptCreateRepo,
17
+ showUserRepos,
18
+ } from '../lib/commands/github.js';
19
+ import { selectBox } from '../lib/prompt-helpers.js';
20
+ import {
21
+ printBanner,
22
+ printDashboardStatus,
23
+ printGitMissing,
24
+ printCancelled,
25
+ styleCliHelp
26
+ } from '../lib/ui.js';
27
+
28
+ const __filename = fileURLToPath(import.meta.url);
29
+ const __dirname = path.dirname(__filename);
30
+
31
+ function getPackageVersion() {
32
+ try {
33
+ const raw = fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8');
34
+ return JSON.parse(raw).version || '1.1.0';
35
+ } catch {
36
+ return '1.1.0';
37
+ }
38
+ }
39
+
40
+ const VERSION = getPackageVersion();
41
+
42
+ function isExitPromptError(err) {
43
+ const msg = String(err?.message || '');
44
+ return (
45
+ err?.name === 'ExitPromptError' ||
46
+ msg.includes('User force closed the prompt') ||
47
+ msg.includes('SIGINT')
48
+ );
49
+ }
50
+
51
+ /**
52
+ * Check whether the command was invoked without flags or subcommands.
53
+ */
54
+ function isBareInvocation(options = {}) {
55
+ return (
56
+ !options.repo &&
57
+ !options.message &&
58
+ !options.date &&
59
+ !options.branch &&
60
+ !options.dir &&
61
+ !options.force &&
62
+ !options.fix
63
+ );
64
+ }
65
+
66
+ /**
67
+ * Main menu shown when `git-push-time` is run without flags.
68
+ */
69
+ async function promptMainMenu() {
70
+ return await selectBox({
71
+ message: 'Main dashboard',
72
+ hint: '↑↓ move · Enter select · Esc exit',
73
+ pageSize: 10,
74
+ choices: [
75
+ {
76
+ name: `${chalk.hex('#38BDF8').bold('PUSH')} ${chalk.hex('#334155')('·')} ${chalk.hex('#F1F5F9')('Push a project to a repository')}`,
77
+ value: 'push',
78
+ description: 'Choose or create a repository, scan for secrets, and push',
79
+ },
80
+ {
81
+ name: `${chalk.hex('#A78BFA').bold('GITHUB')} ${chalk.hex('#334155')('·')} ${chalk.hex('#CBD5E1')('Accounts and repositories')}`,
82
+ value: 'github',
83
+ description: 'Sign in, switch accounts, and browse GitHub repositories',
84
+ },
85
+ {
86
+ name: `${chalk.hex('#34D399').bold('PULL')} ${chalk.hex('#334155')('·')} ${chalk.hex('#CBD5E1')('Get updates from a remote')}`,
87
+ value: 'pull',
88
+ description: 'Pull safely with fast-forward or rebase',
89
+ },
90
+ {
91
+ name: `${chalk.hex('#FBBF24').bold('EDIT TIME')} ${chalk.hex('#334155')('·')} ${chalk.hex('#CBD5E1')('Change a commit timestamp')}`,
92
+ value: 'edit-time',
93
+ description: 'Rewrite the timestamp of an existing commit',
94
+ },
95
+ {
96
+ name: `${chalk.hex('#F43F5E').bold('SECURITY')} ${chalk.hex('#334155')('·')} ${chalk.hex('#CBD5E1')('Scan for secrets')}`,
97
+ value: 'fix',
98
+ description: 'Scan for secrets, unstage them, and update .gitignore',
99
+ },
100
+ {
101
+ name: `${chalk.hex('#64748B').bold('EXIT')} ${chalk.hex('#334155')('·')} ${chalk.hex('#64748B')('Close the program')}`,
102
+ value: 'exit',
103
+ },
104
+ ],
105
+ });
106
+ }
107
+
108
+ /**
109
+ * Main menu loop; back callbacks return a sentinel instead of recursing.
110
+ */
111
+ async function runMainMenuLoop(initialOptions = {}) {
112
+ while (true) {
113
+ const cwd = process.cwd();
114
+ let branch = 'main';
115
+ try {
116
+ branch = getCurrentBranch(cwd);
117
+ } catch {}
118
+ let ghStatus = { authenticated: false };
119
+ try {
120
+ ghStatus = await getCurrentGitHubUser();
121
+ } catch {
122
+ ghStatus = { authenticated: false };
123
+ }
124
+
125
+ const gitStatus = getDashboardGitStatus(cwd);
126
+ printDashboardStatus({
127
+ targetDir: cwd,
128
+ currentBranch: branch,
129
+ githubUser: ghStatus.authenticated ? ghStatus.user : null,
130
+ remoteUrl: gitStatus.remoteUrl,
131
+ changes: gitStatus.changes,
132
+ lastCommit: gitStatus.lastCommit,
133
+ version: VERSION,
134
+ });
135
+ let action;
136
+ try {
137
+ action = await promptMainMenu();
138
+ } catch (err) {
139
+ if (isExitPromptError(err)) {
140
+ printCancelled();
141
+ process.exit(0);
142
+ }
143
+ throw err;
144
+ }
145
+
146
+ if (action === 'exit') {
147
+ printCancelled();
148
+ process.exit(0);
149
+ }
150
+
151
+ try {
152
+ if (action === 'push') {
153
+ const res = await runPushWizard(initialOptions, async () => 'back');
154
+ // 'back' returns to the dashboard; otherwise the completed push exits.
155
+ if (res === 'back') continue;
156
+ return;
157
+ }
158
+
159
+ if (action === 'github') {
160
+ await runGitHubMenu();
161
+ } else if (action === 'pull') {
162
+ await runPullCommand('origin', undefined, { fromMenu: true });
163
+ } else if (action === 'edit-time') {
164
+ await runEditCommitTimeCommand({ fromMenu: true });
165
+ } else if (action === 'fix') {
166
+ await runFixCommand({ fromMenu: true });
167
+ }
168
+ } catch (err) {
169
+ if (isExitPromptError(err)) {
170
+ printCancelled();
171
+ process.exit(0);
172
+ }
173
+ throw err;
174
+ }
175
+
176
+ // Each action returns to the dashboard, so no extra continue/back prompt is needed.
177
+ }
178
+ }
179
+
180
+ async function main(options = {}) {
181
+ printBanner(VERSION);
182
+
183
+ if (!checkGitInstalled()) {
184
+ printGitMissing();
185
+ process.exit(1);
186
+ }
187
+
188
+ const isInteractive = !options.yes;
189
+
190
+ if (isInteractive && isBareInvocation(options)) {
191
+ try {
192
+ await runMainMenuLoop(options);
193
+ } catch (err) {
194
+ if (isExitPromptError(err)) {
195
+ printCancelled();
196
+ process.exit(0);
197
+ }
198
+ throw err;
199
+ }
200
+ return;
201
+ }
202
+
203
+ // Run the push wizard directly with the supplied options.
204
+ try {
205
+ await runPushWizard(options);
206
+ } catch (err) {
207
+ if (isExitPromptError(err)) {
208
+ printCancelled();
209
+ process.exit(0);
210
+ }
211
+ throw err;
212
+ }
213
+ }
214
+
215
+ /* ==========================================================================
216
+ COMMANDER CLI CONFIGURATION
217
+ ========================================================================== */
218
+
219
+ const program = new Command();
220
+ styleCliHelp(program);
221
+
222
+ program
223
+ .name('git-push-time')
224
+ .description('Git and GitHub CLI for pushing, repository management, commit timestamps, and secret protection')
225
+ .version(VERSION)
226
+ .option('-r, --repo <url>', 'Git repository URL, or leave empty to choose or create one')
227
+ .option('-d, --date <datetime>', 'Commit time (e.g. "2024-01-15 14:30:00", "-2d", "-5h", "now")')
228
+ .option('-m, --message <msg>', 'Commit message')
229
+ .option('-b, --branch <branch>', 'Remote branch name (default: main)')
230
+ .option('-C, --dir <path>', 'Source folder to push (default: current folder)')
231
+ .option('-f, --force', 'Force push (--force)', false)
232
+ .option('-y, --yes', 'Skip prompts and use the provided values or defaults', false)
233
+ .option('--fix', 'Unstage secret files and update .gitignore (keeps local files)', false)
234
+ .action(async (opts) => {
235
+ await main(opts);
236
+ });
237
+
238
+ // GITHUB COMMANDS
239
+ const ghCmd = program
240
+ .command('github')
241
+ .description('Manage GitHub accounts and repositories')
242
+ .action(async () => {
243
+ printBanner(VERSION);
244
+ await runGitHubMenu();
245
+ });
246
+
247
+ ghCmd
248
+ .command('create')
249
+ .description('Create a GitHub repository (public or private)')
250
+ .option('-C, --dir <path>', 'Source folder used to suggest a repository name')
251
+ .action(async (opts) => {
252
+ printBanner(VERSION);
253
+ const res = await promptCreateRepo({ targetDir: opts.dir || process.cwd() });
254
+ if (res.ok && res.repo) {
255
+ console.log(`\n » Repo URL: ${res.repo.cloneUrl}\n`);
256
+ }
257
+ });
258
+
259
+ ghCmd
260
+ .command('whoami')
261
+ .description('Show the connected GitHub account')
262
+ .action(async () => {
263
+ printBanner(VERSION);
264
+ await showGitHubStatus();
265
+ });
266
+
267
+ ghCmd
268
+ .command('repos')
269
+ .description('List your GitHub repositories')
270
+ .action(async () => {
271
+ printBanner(VERSION);
272
+ await showUserRepos();
273
+ });
274
+
275
+ ghCmd
276
+ .command('login')
277
+ .description('Sign in to GitHub with OAuth Device Flow or a token')
278
+ .action(async () => {
279
+ printBanner(VERSION);
280
+ await promptGitHubLogin();
281
+ });
282
+
283
+ ghCmd
284
+ .command('logout')
285
+ .description('Sign out of GitHub on this machine')
286
+ .action(async () => {
287
+ printBanner(VERSION);
288
+ logoutGitHub();
289
+ });
290
+
291
+ // FIX COMMAND
292
+ program
293
+ .command('fix')
294
+ .description('Scan for secrets, unstage files, update .gitignore, and clean unpushed commits')
295
+ .option('-C, --dir <path>', 'Folder to scan (default: current folder)')
296
+ .action(async (opts) => {
297
+ printBanner(VERSION);
298
+ await runFixCommand(opts);
299
+ });
300
+
301
+ // PULL COMMAND
302
+ program
303
+ .command('pull [remote] [branch]')
304
+ .description('Pull from a remote with fast-forward (--ff-only) or rebase')
305
+ .option('-C, --dir <path>', 'Repository folder (default: current folder)')
306
+ .action(async (remote, branch, opts) => {
307
+ printBanner(VERSION);
308
+ await runPullCommand(remote || 'origin', branch, opts || {});
309
+ });
310
+
311
+ // EDIT-TIME COMMAND
312
+ program
313
+ .command('edit-time')
314
+ .description('Change the timestamp of an existing commit')
315
+ .option('-C, --dir <path>', 'Repository folder (default: current folder)')
316
+ .action(async (opts) => {
317
+ printBanner(VERSION);
318
+ await runEditCommitTimeCommand(opts);
319
+ });
320
+
321
+ program.parse(process.argv);