@ecmaos/coreutils 0.5.2 → 0.5.3

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.
@@ -0,0 +1,708 @@
1
+ import path from 'path';
2
+ import chalk from 'chalk';
3
+ import * as git from 'isomorphic-git';
4
+ import http from 'isomorphic-git/http/web';
5
+ import { TerminalCommand } from '../shared/terminal-command.js';
6
+ import { writelnStdout, writelnStderr } from '../shared/helpers.js';
7
+ const CORS_PROXY = 'https://cors.isomorphic-git.org';
8
+ function printUsage(process, terminal) {
9
+ const usage = `Usage: git [COMMAND] [OPTIONS] [ARGS...]
10
+
11
+ Common Git commands:
12
+ init Initialize a new repository
13
+ clone <url> Clone a repository
14
+ add <file>... Add files to staging
15
+ commit -m <msg> Commit staged changes
16
+ status Show working tree status
17
+ log Show commit logs
18
+ branch List or create branches
19
+ checkout <branch> Switch branches
20
+ push Push to remote
21
+ pull Pull from remote
22
+ fetch Fetch from remote
23
+ diff Show changes
24
+ rm <file>... Remove files from git
25
+ config Get/set configuration
26
+ remote Manage remotes
27
+
28
+ --help display this help and exit`;
29
+ writelnStderr(process, terminal, usage);
30
+ }
31
+ async function findGitDir(fs, startDir) {
32
+ let currentDir = startDir;
33
+ const root = '/';
34
+ while (currentDir !== root && currentDir !== '') {
35
+ const gitDir = path.join(currentDir, '.git');
36
+ try {
37
+ await fs.stat(gitDir);
38
+ return gitDir;
39
+ }
40
+ catch {
41
+ const parent = path.dirname(currentDir);
42
+ if (parent === currentDir)
43
+ break;
44
+ currentDir = parent;
45
+ }
46
+ }
47
+ return null;
48
+ }
49
+ async function getGitDir(fs, cwd) {
50
+ const gitDir = await findGitDir(fs, cwd);
51
+ if (!gitDir) {
52
+ throw new Error('not a git repository (or any of the parent directories)');
53
+ }
54
+ return path.dirname(gitDir);
55
+ }
56
+ async function handleInit(fs, shell, terminal, process, args) {
57
+ const dir = args.length > 0 && args[0] ? path.resolve(shell.cwd, args[0]) : shell.cwd;
58
+ try {
59
+ await git.init({ fs, dir });
60
+ await writelnStdout(process, terminal, `Initialized empty Git repository in ${dir}/.git/`);
61
+ return 0;
62
+ }
63
+ catch (error) {
64
+ const errorMessage = error instanceof Error ? error.message : String(error);
65
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
66
+ return 1;
67
+ }
68
+ }
69
+ function convertSshToHttps(url) {
70
+ const sshPattern = /^git@([^:]+):(.+)$/;
71
+ const match = url.match(sshPattern);
72
+ if (match) {
73
+ const host = match[1];
74
+ const path = match[2];
75
+ return `https://${host}/${path}`;
76
+ }
77
+ return url;
78
+ }
79
+ async function handleClone(fs, shell, terminal, process, args) {
80
+ if (args.length === 0) {
81
+ await writelnStderr(process, terminal, 'fatal: You must specify a repository to clone.');
82
+ return 1;
83
+ }
84
+ const url = args[0];
85
+ if (!url) {
86
+ await writelnStderr(process, terminal, 'fatal: You must specify a repository to clone.');
87
+ return 1;
88
+ }
89
+ const httpsUrl = convertSshToHttps(url);
90
+ const dir = args.length > 1 && args[1]
91
+ ? path.resolve(shell.cwd, args[1])
92
+ : path.resolve(shell.cwd, path.basename(httpsUrl.replace(/\.git$/, '')));
93
+ try {
94
+ await writelnStdout(process, terminal, `Cloning into '${path.basename(dir)}'...`);
95
+ const token = shell.env.get('GITHUB_TOKEN');
96
+ await git.clone({
97
+ fs,
98
+ http,
99
+ dir,
100
+ url: httpsUrl,
101
+ corsProxy: shell.env.get('GIT_CORS_PROXY') || CORS_PROXY,
102
+ onAuth: token ? () => ({ username: token }) : undefined
103
+ });
104
+ await writelnStdout(process, terminal, 'done.');
105
+ return 0;
106
+ }
107
+ catch (error) {
108
+ const errorMessage = error instanceof Error ? error.message : String(error);
109
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
110
+ return 1;
111
+ }
112
+ }
113
+ async function collectFilesRecursively(fs, searchDir, gitDir) {
114
+ const files = [];
115
+ try {
116
+ const entries = await fs.readdir(searchDir);
117
+ for (const entry of entries) {
118
+ if (entry === '.git')
119
+ continue;
120
+ const entryPath = path.join(searchDir, entry);
121
+ let entryStats;
122
+ try {
123
+ entryStats = await fs.stat(entryPath);
124
+ }
125
+ catch {
126
+ continue;
127
+ }
128
+ if (entryStats.isDirectory()) {
129
+ const subFiles = await collectFilesRecursively(fs, entryPath, gitDir);
130
+ files.push(...subFiles);
131
+ }
132
+ else if (entryStats.isFile()) {
133
+ const gitRelativePath = path.relative(gitDir, entryPath).replace(/\\/g, '/');
134
+ if (gitRelativePath && !gitRelativePath.startsWith('..') && gitRelativePath !== '.git' && !gitRelativePath.startsWith('.git/')) {
135
+ files.push(gitRelativePath);
136
+ }
137
+ }
138
+ }
139
+ }
140
+ catch {
141
+ // Skip directories that can't be accessed
142
+ }
143
+ return files;
144
+ }
145
+ async function handleAdd(fs, shell, terminal, process, args) {
146
+ if (args.length === 0) {
147
+ await writelnStderr(process, terminal, 'Nothing specified, nothing added.');
148
+ return 0;
149
+ }
150
+ try {
151
+ const dir = await getGitDir(fs, shell.cwd);
152
+ const filesToAdd = new Set();
153
+ for (const file of args) {
154
+ if (!file)
155
+ continue;
156
+ const targetPath = path.resolve(shell.cwd, file);
157
+ try {
158
+ const stats = await fs.stat(targetPath);
159
+ if (stats.isDirectory()) {
160
+ const collectedFiles = await collectFilesRecursively(fs, targetPath, dir);
161
+ for (const filePath of collectedFiles) {
162
+ if (filePath && filePath !== '.git' && !filePath.startsWith('.git/')) {
163
+ filesToAdd.add(filePath);
164
+ }
165
+ }
166
+ }
167
+ else if (stats.isFile()) {
168
+ const gitRelativePath = path.relative(dir, targetPath).replace(/\\/g, '/');
169
+ if (gitRelativePath && !gitRelativePath.startsWith('..') && gitRelativePath !== '.git' && !gitRelativePath.startsWith('.git/')) {
170
+ filesToAdd.add(gitRelativePath);
171
+ }
172
+ }
173
+ }
174
+ catch {
175
+ continue;
176
+ }
177
+ }
178
+ let hasError = false;
179
+ for (const filePath of filesToAdd) {
180
+ try {
181
+ await git.add({ fs, dir, filepath: filePath });
182
+ }
183
+ catch (error) {
184
+ const errorMessage = error instanceof Error ? error.message : String(error);
185
+ await writelnStderr(process, terminal, `error: ${errorMessage}`);
186
+ hasError = true;
187
+ }
188
+ }
189
+ return hasError ? 1 : 0;
190
+ }
191
+ catch (error) {
192
+ const errorMessage = error instanceof Error ? error.message : String(error);
193
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
194
+ return 1;
195
+ }
196
+ }
197
+ async function handleCommit(fs, shell, terminal, process, args) {
198
+ let message;
199
+ for (let i = 0; i < args.length; i++) {
200
+ if (args[i] === '-m' && i + 1 < args.length) {
201
+ message = args[i + 1];
202
+ i++;
203
+ }
204
+ else if (args[i] === '--message' && i + 1 < args.length) {
205
+ message = args[i + 1];
206
+ i++;
207
+ }
208
+ else if (args[i]?.startsWith('-m')) {
209
+ message = args[i]?.slice(2) || undefined;
210
+ }
211
+ }
212
+ if (!message) {
213
+ await writelnStderr(process, terminal, 'Aborting commit due to empty commit message.');
214
+ return 1;
215
+ }
216
+ try {
217
+ const dir = await getGitDir(fs, shell.cwd);
218
+ const username = shell.env.get('USER') || 'root';
219
+ const email = shell.env.get('EMAIL') || `${username}@${shell.env.get('HOSTNAME') || 'localhost'}`;
220
+ const sha = await git.commit({
221
+ fs,
222
+ dir,
223
+ message,
224
+ author: {
225
+ name: username,
226
+ email
227
+ }
228
+ });
229
+ await writelnStdout(process, terminal, `[${sha.slice(0, 7)}] ${message}`);
230
+ return 0;
231
+ }
232
+ catch (error) {
233
+ const errorMessage = error instanceof Error ? error.message : String(error);
234
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
235
+ return 1;
236
+ }
237
+ }
238
+ async function handleStatus(fs, shell, terminal, process, _args) {
239
+ try {
240
+ const dir = await getGitDir(fs, shell.cwd);
241
+ const statusMatrix = await git.statusMatrix({ fs, dir });
242
+ const modified = [];
243
+ const added = [];
244
+ const deleted = [];
245
+ const untracked = [];
246
+ for (const [filepath, headStatus, workdirStatus, stageStatus] of statusMatrix) {
247
+ if (headStatus === 0 && stageStatus === 2) {
248
+ added.push(filepath);
249
+ }
250
+ else if (headStatus === 1 && workdirStatus === 0) {
251
+ deleted.push(filepath);
252
+ }
253
+ else if (headStatus === 1 && workdirStatus === 2) {
254
+ modified.push(filepath);
255
+ }
256
+ else if (headStatus === 0 && workdirStatus === 2 && stageStatus === 0) {
257
+ untracked.push(filepath);
258
+ }
259
+ }
260
+ if (modified.length === 0 && added.length === 0 && deleted.length === 0 && untracked.length === 0) {
261
+ await writelnStdout(process, terminal, 'nothing to commit, working tree clean');
262
+ return 0;
263
+ }
264
+ if (modified.length > 0) {
265
+ await writelnStdout(process, terminal, chalk.red('modified: ') + modified.join(' '));
266
+ }
267
+ if (added.length > 0) {
268
+ await writelnStdout(process, terminal, chalk.green('new file: ') + added.join(' '));
269
+ }
270
+ if (deleted.length > 0) {
271
+ await writelnStdout(process, terminal, chalk.red('deleted: ') + deleted.join(' '));
272
+ }
273
+ if (untracked.length > 0) {
274
+ await writelnStdout(process, terminal, chalk.yellow('untracked: ') + untracked.join(' '));
275
+ }
276
+ return 0;
277
+ }
278
+ catch (error) {
279
+ const errorMessage = error instanceof Error ? error.message : String(error);
280
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
281
+ return 1;
282
+ }
283
+ }
284
+ async function handleLog(fs, shell, terminal, process, args) {
285
+ let depth = 10;
286
+ let oneline = false;
287
+ for (let i = 0; i < args.length; i++) {
288
+ if (args[i] === '--oneline') {
289
+ oneline = true;
290
+ }
291
+ else if (args[i] === '-n' && i + 1 < args.length) {
292
+ depth = parseInt(args[i + 1] || '10', 10) || depth;
293
+ i++;
294
+ }
295
+ else if (args[i]?.startsWith('-n')) {
296
+ depth = parseInt(args[i]?.slice(2) || '10', 10) || depth;
297
+ }
298
+ }
299
+ try {
300
+ const dir = await getGitDir(fs, shell.cwd);
301
+ const commits = await git.log({ fs, dir, depth });
302
+ for (const commit of commits) {
303
+ const commitObj = await git.readCommit({ fs, dir, oid: commit.oid });
304
+ if (oneline) {
305
+ await writelnStdout(process, terminal, `${chalk.yellow(commit.oid.slice(0, 7))} ${commitObj.commit.message.split('\n')[0]}`);
306
+ }
307
+ else {
308
+ await writelnStdout(process, terminal, `commit ${chalk.yellow(commit.oid)}`);
309
+ await writelnStdout(process, terminal, `Author: ${commitObj.commit.author.name} <${commitObj.commit.author.email}>`);
310
+ await writelnStdout(process, terminal, `Date: ${new Date(commitObj.commit.author.timestamp * 1000).toLocaleString()}`);
311
+ await writelnStdout(process, terminal, '');
312
+ for (const line of commitObj.commit.message.split('\n')) {
313
+ await writelnStdout(process, terminal, ` ${line}`);
314
+ }
315
+ await writelnStdout(process, terminal, '');
316
+ }
317
+ }
318
+ return 0;
319
+ }
320
+ catch (error) {
321
+ const errorMessage = error instanceof Error ? error.message : String(error);
322
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
323
+ return 1;
324
+ }
325
+ }
326
+ async function handleBranch(fs, shell, terminal, process, args) {
327
+ try {
328
+ const dir = await getGitDir(fs, shell.cwd);
329
+ if (args.length === 0) {
330
+ const branches = await git.listBranches({ fs, dir });
331
+ const currentBranch = await git.currentBranch({ fs, dir });
332
+ for (const branch of branches) {
333
+ if (branch === currentBranch) {
334
+ await writelnStdout(process, terminal, chalk.green(`* ${branch}`));
335
+ }
336
+ else {
337
+ await writelnStdout(process, terminal, ` ${branch}`);
338
+ }
339
+ }
340
+ return 0;
341
+ }
342
+ const branchName = args[0];
343
+ if (!branchName) {
344
+ await writelnStderr(process, terminal, 'fatal: branch name required');
345
+ return 1;
346
+ }
347
+ await git.branch({ fs, dir, ref: branchName, checkout: false });
348
+ await writelnStdout(process, terminal, `Created branch '${branchName}'`);
349
+ return 0;
350
+ }
351
+ catch (error) {
352
+ const errorMessage = error instanceof Error ? error.message : String(error);
353
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
354
+ return 1;
355
+ }
356
+ }
357
+ async function handleCheckout(fs, shell, terminal, process, args) {
358
+ if (args.length === 0) {
359
+ await writelnStderr(process, terminal, 'fatal: You must specify a branch to checkout.');
360
+ return 1;
361
+ }
362
+ try {
363
+ const dir = await getGitDir(fs, shell.cwd);
364
+ const branch = args[0];
365
+ await git.checkout({ fs, dir, ref: branch });
366
+ await writelnStdout(process, terminal, `Switched to branch '${branch}'`);
367
+ return 0;
368
+ }
369
+ catch (error) {
370
+ const errorMessage = error instanceof Error ? error.message : String(error);
371
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
372
+ return 1;
373
+ }
374
+ }
375
+ async function handlePush(fs, shell, terminal, process, args) {
376
+ try {
377
+ const dir = await getGitDir(fs, shell.cwd);
378
+ const remote = args[0] || 'origin';
379
+ const currentBranch = await git.currentBranch({ fs, dir });
380
+ const ref = args[1] || currentBranch || 'main';
381
+ if (!ref) {
382
+ await writelnStderr(process, terminal, 'fatal: No branch specified and unable to determine current branch.');
383
+ return 1;
384
+ }
385
+ await writelnStdout(process, terminal, `Pushing to ${remote}...`);
386
+ const token = shell.env.get('GITHUB_TOKEN');
387
+ await git.push({
388
+ fs,
389
+ http,
390
+ dir,
391
+ remote,
392
+ ref,
393
+ corsProxy: CORS_PROXY,
394
+ onAuth: token ? () => ({ username: token }) : undefined
395
+ });
396
+ await writelnStdout(process, terminal, 'done.');
397
+ return 0;
398
+ }
399
+ catch (error) {
400
+ const errorMessage = error instanceof Error ? error.message : String(error);
401
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
402
+ return 1;
403
+ }
404
+ }
405
+ async function handlePull(fs, shell, terminal, process, args) {
406
+ try {
407
+ const dir = await getGitDir(fs, shell.cwd);
408
+ const remote = args[0] || 'origin';
409
+ const currentBranch = await git.currentBranch({ fs, dir });
410
+ const ref = args[1] || currentBranch || 'main';
411
+ if (!ref) {
412
+ await writelnStderr(process, terminal, 'fatal: No branch specified and unable to determine current branch.');
413
+ return 1;
414
+ }
415
+ await writelnStdout(process, terminal, `Pulling from ${remote}...`);
416
+ const token = shell.env.get('GITHUB_TOKEN');
417
+ await git.pull({
418
+ fs,
419
+ http,
420
+ dir,
421
+ remote,
422
+ ref,
423
+ corsProxy: CORS_PROXY,
424
+ onAuth: token ? () => ({ username: token }) : undefined
425
+ });
426
+ await writelnStdout(process, terminal, 'done.');
427
+ return 0;
428
+ }
429
+ catch (error) {
430
+ const errorMessage = error instanceof Error ? error.message : String(error);
431
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
432
+ return 1;
433
+ }
434
+ }
435
+ async function handleFetch(fs, shell, terminal, process, args) {
436
+ try {
437
+ const dir = await getGitDir(fs, shell.cwd);
438
+ const remote = args[0] || 'origin';
439
+ await writelnStdout(process, terminal, `Fetching from ${remote}...`);
440
+ const token = shell.env.get('GITHUB_TOKEN');
441
+ await git.fetch({
442
+ fs,
443
+ http,
444
+ dir,
445
+ remote,
446
+ corsProxy: CORS_PROXY,
447
+ onAuth: token ? () => ({ username: token }) : undefined
448
+ });
449
+ await writelnStdout(process, terminal, 'done.');
450
+ return 0;
451
+ }
452
+ catch (error) {
453
+ const errorMessage = error instanceof Error ? error.message : String(error);
454
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
455
+ return 1;
456
+ }
457
+ }
458
+ async function handleDiff(fs, shell, terminal, process, args) {
459
+ try {
460
+ const dir = await getGitDir(fs, shell.cwd);
461
+ if (args.length > 0 && args[0]) {
462
+ const filepath = path.relative(dir, path.resolve(shell.cwd, args[0]));
463
+ const status = await git.status({ fs, dir, filepath });
464
+ if (status === '*modified' || status === '*added' || status === '*deleted') {
465
+ await writelnStdout(process, terminal, `diff --git a/${filepath} b/${filepath}`);
466
+ await writelnStdout(process, terminal, `--- a/${filepath}`);
467
+ await writelnStdout(process, terminal, `+++ b/${filepath}`);
468
+ await writelnStdout(process, terminal, `Status: ${status}`);
469
+ }
470
+ else {
471
+ await writelnStdout(process, terminal, `No changes to ${filepath}`);
472
+ }
473
+ }
474
+ else {
475
+ const statusMatrix = await git.statusMatrix({ fs, dir });
476
+ for (const [filepath] of statusMatrix) {
477
+ await writelnStdout(process, terminal, `diff --git a/${filepath} b/${filepath}`);
478
+ }
479
+ }
480
+ return 0;
481
+ }
482
+ catch (error) {
483
+ const errorMessage = error instanceof Error ? error.message : String(error);
484
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
485
+ return 1;
486
+ }
487
+ }
488
+ async function handleRm(fs, shell, terminal, process, args) {
489
+ if (args.length === 0) {
490
+ await writelnStderr(process, terminal, 'Nothing specified, nothing removed.');
491
+ return 0;
492
+ }
493
+ try {
494
+ const dir = await getGitDir(fs, shell.cwd);
495
+ for (const file of args) {
496
+ if (!file)
497
+ continue;
498
+ const filePath = path.relative(dir, path.resolve(shell.cwd, file));
499
+ try {
500
+ await git.remove({ fs, dir, filepath: filePath });
501
+ }
502
+ catch (error) {
503
+ const errorMessage = error instanceof Error ? error.message : String(error);
504
+ await writelnStderr(process, terminal, `error: ${errorMessage}`);
505
+ }
506
+ }
507
+ return 0;
508
+ }
509
+ catch (error) {
510
+ const errorMessage = error instanceof Error ? error.message : String(error);
511
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
512
+ return 1;
513
+ }
514
+ }
515
+ async function handleRemote(fs, shell, terminal, process, args) {
516
+ try {
517
+ const dir = await getGitDir(fs, shell.cwd);
518
+ if (args.length === 0) {
519
+ const remotes = await git.listRemotes({ fs, dir });
520
+ for (const remote of remotes) {
521
+ await writelnStdout(process, terminal, remote.remote);
522
+ }
523
+ return 0;
524
+ }
525
+ if (args[0] === '-v' || args[0] === '--verbose') {
526
+ const remotes = await git.listRemotes({ fs, dir });
527
+ for (const remote of remotes) {
528
+ await writelnStdout(process, terminal, `${remote.remote}\t${remote.url} (fetch)`);
529
+ await writelnStdout(process, terminal, `${remote.remote}\t${remote.url} (push)`);
530
+ }
531
+ return 0;
532
+ }
533
+ if (args[0] === 'add' && args.length === 3 && args[1] && args[2]) {
534
+ const httpsUrl = convertSshToHttps(args[2]);
535
+ await git.setConfig({ fs, dir, path: `remote.${args[1]}.url`, value: httpsUrl });
536
+ return 0;
537
+ }
538
+ if ((args[0] === 'remove' || args[0] === 'rm') && args.length === 2 && args[1]) {
539
+ try {
540
+ const configFile = path.join(dir, '.git', 'config');
541
+ const configContent = await fs.readFile(configFile, 'utf-8');
542
+ const lines = configContent.split('\n');
543
+ const newLines = [];
544
+ let skipSection = false;
545
+ for (let i = 0; i < lines.length; i++) {
546
+ const line = lines[i];
547
+ if (!line)
548
+ continue;
549
+ if (line.trim() === `[remote "${args[1]}"]`) {
550
+ skipSection = true;
551
+ continue;
552
+ }
553
+ if (skipSection && line.trim().startsWith('[')) {
554
+ skipSection = false;
555
+ }
556
+ if (!skipSection) {
557
+ newLines.push(line);
558
+ }
559
+ }
560
+ await fs.writeFile(configFile, newLines.join('\n'), 'utf-8');
561
+ }
562
+ catch (error) {
563
+ const errorMessage = error instanceof Error ? error.message : String(error);
564
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
565
+ return 1;
566
+ }
567
+ return 0;
568
+ }
569
+ if (args[0] === 'set-url' && args.length === 3 && args[1] && args[2]) {
570
+ const httpsUrl = convertSshToHttps(args[2]);
571
+ await git.setConfig({ fs, dir, path: `remote.${args[1]}.url`, value: httpsUrl });
572
+ return 0;
573
+ }
574
+ if (args[0] === 'show' && args.length === 2 && args[1]) {
575
+ try {
576
+ const url = await git.getConfig({ fs, dir, path: `remote.${args[1]}.url` });
577
+ if (url) {
578
+ await writelnStdout(process, terminal, `* remote ${args[1]}`);
579
+ await writelnStdout(process, terminal, ` Fetch URL: ${url}`);
580
+ await writelnStdout(process, terminal, ` Push URL: ${url}`);
581
+ }
582
+ else {
583
+ await writelnStderr(process, terminal, `fatal: No such remote '${args[1]}'`);
584
+ return 1;
585
+ }
586
+ }
587
+ catch (error) {
588
+ const errorMessage = error instanceof Error ? error.message : String(error);
589
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
590
+ return 1;
591
+ }
592
+ return 0;
593
+ }
594
+ await writelnStderr(process, terminal, 'usage: git remote [-v | --verbose]');
595
+ await writelnStderr(process, terminal, ' or: git remote add <name> <url>');
596
+ await writelnStderr(process, terminal, ' or: git remote remove <name>');
597
+ await writelnStderr(process, terminal, ' or: git remote set-url <name> <url>');
598
+ await writelnStderr(process, terminal, ' or: git remote show <name>');
599
+ return 1;
600
+ }
601
+ catch (error) {
602
+ const errorMessage = error instanceof Error ? error.message : String(error);
603
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
604
+ return 1;
605
+ }
606
+ }
607
+ async function handleConfig(fs, shell, terminal, process, args) {
608
+ try {
609
+ const dir = await getGitDir(fs, shell.cwd);
610
+ if (args.length === 0) {
611
+ try {
612
+ const configFile = path.join(dir, '.git', 'config');
613
+ const configContent = await fs.readFile(configFile, 'utf-8');
614
+ const lines = configContent.split('\n');
615
+ for (const line of lines) {
616
+ const trimmed = line.trim();
617
+ if (trimmed && !trimmed.startsWith('[') && !trimmed.startsWith('#') && trimmed.includes('=')) {
618
+ await writelnStdout(process, terminal, trimmed);
619
+ }
620
+ }
621
+ }
622
+ catch {
623
+ await writelnStdout(process, terminal, 'No configuration found');
624
+ }
625
+ return 0;
626
+ }
627
+ if (args.length === 1 && args[0]) {
628
+ const value = await git.getConfig({ fs, dir, path: args[0] });
629
+ if (value) {
630
+ await writelnStdout(process, terminal, value);
631
+ }
632
+ return 0;
633
+ }
634
+ if (args.length === 2 && args[0] && args[1]) {
635
+ await git.setConfig({ fs, dir, path: args[0], value: args[1] });
636
+ return 0;
637
+ }
638
+ await writelnStderr(process, terminal, 'usage: git config <key> [value]');
639
+ return 1;
640
+ }
641
+ catch (error) {
642
+ const errorMessage = error instanceof Error ? error.message : String(error);
643
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
644
+ return 1;
645
+ }
646
+ }
647
+ export function createCommand(kernel, shell, terminal) {
648
+ return new TerminalCommand({
649
+ command: 'git',
650
+ description: 'Git version control system',
651
+ kernel,
652
+ shell,
653
+ terminal,
654
+ run: async (pid, argv) => {
655
+ const process = kernel.processes.get(pid);
656
+ if (argv.length === 0 || (argv.length === 1 && (argv[0] === '--help' || argv[0] === '-h'))) {
657
+ printUsage(process, terminal);
658
+ return 0;
659
+ }
660
+ const subcommand = argv[0];
661
+ const args = argv.slice(1);
662
+ const fs = shell.context.fs.promises;
663
+ try {
664
+ switch (subcommand) {
665
+ case 'init':
666
+ return await handleInit(fs, shell, terminal, process, args);
667
+ case 'clone':
668
+ return await handleClone(fs, shell, terminal, process, args);
669
+ case 'add':
670
+ return await handleAdd(fs, shell, terminal, process, args);
671
+ case 'commit':
672
+ return await handleCommit(fs, shell, terminal, process, args);
673
+ case 'status':
674
+ return await handleStatus(fs, shell, terminal, process, args);
675
+ case 'log':
676
+ return await handleLog(fs, shell, terminal, process, args);
677
+ case 'branch':
678
+ return await handleBranch(fs, shell, terminal, process, args);
679
+ case 'checkout':
680
+ return await handleCheckout(fs, shell, terminal, process, args);
681
+ case 'push':
682
+ return await handlePush(fs, shell, terminal, process, args);
683
+ case 'pull':
684
+ return await handlePull(fs, shell, terminal, process, args);
685
+ case 'fetch':
686
+ return await handleFetch(fs, shell, terminal, process, args);
687
+ case 'diff':
688
+ return await handleDiff(fs, shell, terminal, process, args);
689
+ case 'rm':
690
+ return await handleRm(fs, shell, terminal, process, args);
691
+ case 'config':
692
+ return await handleConfig(fs, shell, terminal, process, args);
693
+ case 'remote':
694
+ return await handleRemote(fs, shell, terminal, process, args);
695
+ default:
696
+ await writelnStderr(process, terminal, `git: '${subcommand}' is not a git command. See 'git --help'.`);
697
+ return 1;
698
+ }
699
+ }
700
+ catch (error) {
701
+ const errorMessage = error instanceof Error ? error.message : String(error);
702
+ await writelnStderr(process, terminal, `fatal: ${errorMessage}`);
703
+ return 1;
704
+ }
705
+ }
706
+ });
707
+ }
708
+ //# sourceMappingURL=git.js.map