-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgit.js
More file actions
100 lines (86 loc) · 3.07 KB
/
Copy pathgit.js
File metadata and controls
100 lines (86 loc) · 3.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
const { execFile } = require('child_process');
let _logger = null;
function setLogger(fn) {
_logger = fn;
}
// Run a git command safely. Only execFile (no shell), arguments as array.
function run(repoPath, args) {
const command = `git ${args.join(' ')}`;
if (_logger) _logger({ type: 'cmd', timestamp: new Date().toISOString(), cwd: repoPath, command });
return new Promise((resolve, reject) => {
execFile('git', args, { cwd: repoPath, timeout: 30000 }, (err, stdout, stderr) => {
if (err) {
if (_logger) _logger({ type: 'error', timestamp: new Date().toISOString(), command, text: stderr.trim() || err.message });
reject(new Error(stderr.trim() || err.message));
} else {
if (_logger && stdout.trim()) _logger({ type: 'output', timestamp: new Date().toISOString(), command, text: stdout.trim() });
resolve(stdout.trim());
}
});
});
}
// Check if git is available on PATH
async function gitVersion() {
const command = 'git --version';
if (_logger) _logger({ type: 'cmd', timestamp: new Date().toISOString(), cwd: null, command });
return new Promise((resolve, reject) => {
execFile('git', ['--version'], { timeout: 5000 }, (err, stdout) => {
if (err) {
if (_logger) _logger({ type: 'error', timestamp: new Date().toISOString(), command, text: 'Git is not installed or not on PATH' });
reject(new Error('Git is not installed or not on PATH'));
} else {
if (_logger && stdout.trim()) _logger({ type: 'output', timestamp: new Date().toISOString(), command, text: stdout.trim() });
resolve(stdout.trim());
}
});
});
}
async function gitFetch(repoPath) {
return run(repoPath, ['fetch', '--all']);
}
async function gitCheckout(repoPath, branch) {
return run(repoPath, ['checkout', branch]);
}
async function gitPull(repoPath) {
return run(repoPath, ['pull']);
}
// Returns raw porcelain output. Empty string = clean.
async function gitStatus(repoPath) {
return run(repoPath, ['status', '--porcelain']);
}
async function gitCurrentBranch(repoPath) {
return run(repoPath, ['rev-parse', '--abbrev-ref', 'HEAD']);
}
async function gitRemoteUrl(repoPath) {
return run(repoPath, ['remote', 'get-url', 'origin']);
}
async function gitBranchList(repoPath) {
const output = await run(repoPath, ['branch', '-a']);
return output.split('\n').map(b => b.replace('*', '').trim()).filter(Boolean);
}
// Parse org/repo from a remote URL
// Supports: https://github.com/org/repo.git, git@github.com:org/repo.git
function parseRepoId(remoteUrl) {
// HTTPS format
let match = remoteUrl.match(/github\.com\/([^/]+\/[^/]+?)(?:\.git)?$/);
if (match) return match[1];
// SSH format
match = remoteUrl.match(/github\.com:([^/]+\/[^/]+?)(?:\.git)?$/);
if (match) return match[1];
// Generic: take last two path segments
match = remoteUrl.match(/\/([^/]+\/[^/]+?)(?:\.git)?$/);
if (match) return match[1];
return null;
}
module.exports = {
setLogger,
gitVersion,
gitFetch,
gitCheckout,
gitPull,
gitStatus,
gitCurrentBranch,
gitRemoteUrl,
gitBranchList,
parseRepoId,
};