-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrebase.js
More file actions
executable file
·186 lines (163 loc) · 5 KB
/
rebase.js
File metadata and controls
executable file
·186 lines (163 loc) · 5 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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const { exec, execSync } = require('child_process');
const LOG = require('./log-tag');
const cwd = process.cwd();
const BRANCH_RECORD_FILE = path.join(__dirname, 'branches.json');
// 동기식 Git 저장소 확인
function isGitRepo(dir) {
return fs.existsSync(path.join(dir, '.git'));
}
// 하위 디렉토리 중 Git 저장소만 추출
function getSubGitDirectories(dir) {
try {
return fs.readdirSync(dir)
.map(f => path.join(dir, f))
.filter(p => fs.statSync(p).isDirectory() && isGitRepo(p));
} catch {
return [];
}
}
// 동기식 현재 브랜치명 추출
function getCurrentBranch(dir) {
try {
return execSync('git rev-parse --abbrev-ref HEAD', { cwd: dir }).toString().trim();
} catch {
return null;
}
}
// 동기식 리모트명 추출 (기본값: origin)
function getRemoteName(dir) {
try {
const remotes = execSync('git remote', { cwd: dir }).toString().trim().split('\n');
return remotes[0] || 'origin';
} catch {
return 'origin';
}
}
// 비동기 명령어 실행 (에러 발생 시 즉시 Reject)
function runCommand(cmd, dir) {
return new Promise((resolve, reject) => {
exec(cmd, { cwd: dir }, (error, stdout, stderr) => {
if (error) {
reject(new Error(stderr ? stderr.trim() : stdout.trim()));
} else {
resolve({ stdout, stderr });
}
});
});
}
// JSON에서 부모 브랜치 추출
function getParentFromJson(branchName) {
try {
if (!fs.existsSync(BRANCH_RECORD_FILE)) return null;
const data = JSON.parse(fs.readFileSync(BRANCH_RECORD_FILE, 'utf-8'));
return data[branchName] || null;
} catch {
return null;
}
}
// 핵심 로직: Fetch 및 Rebase
async function fetchRebase(dir, targetBranch) {
const name = path.basename(dir);
const remote = getRemoteName(dir);
console.log(`${LOG.info} Fetching "${name}" (${remote}/${targetBranch})...`);
try {
await runCommand(`git fetch ${remote} ${targetBranch}`, dir);
} catch (err) {
console.warn(`${LOG.warn} [${name}] Fetch failed: ${err.message}`);
throw err;
}
console.log(`${LOG.info} Rebasing "${name}" onto "${remote}/${targetBranch}"...`);
try {
const { stdout, stderr } = await runCommand(`git rebase ${remote}/${targetBranch}`, dir);
const output = (stdout || '') + (stderr || '');
if (/is up to date\./i.test(output)) {
console.log(`${LOG.info} No changes to rebase in "${name}" (${targetBranch}).\n`);
} else {
console.log(`${LOG.ok} Rebase completed in "${name}" (${targetBranch}).\n`);
}
} catch (err) {
console.error(`\n${LOG.error} [${name}] Rebase failed (Conflict or Error).`);
console.error(`${LOG.hint} Resolve conflicts manually in "${name}" and run 'git rebase --continue'.\n`);
throw err;
}
}
// Mode: this
async function doThis() {
if (!isGitRepo(cwd)) {
console.error(`${LOG.error} Current directory is not a Git repository.`);
process.exit(1);
}
const branch = getCurrentBranch(cwd);
if (!branch) {
console.error(`${LOG.error} Could not resolve current branch.`);
process.exit(1);
}
await fetchRebase(cwd, branch);
}
// Mode: all
async function doAll() {
let hasError = false;
const targets = [];
if (isGitRepo(cwd)) targets.push(cwd);
targets.push(...getSubGitDirectories(cwd));
for (const d of targets) {
const branch = getCurrentBranch(d);
if (branch) {
try {
await fetchRebase(d, branch);
} catch {
hasError = true; // 에러가 발생해도 다음 저장소의 rebase는 시도함
}
}
}
if (hasError) process.exit(1);
}
// Mode: parent
async function doParent() {
if (!isGitRepo(cwd)) {
console.error(`${LOG.error} Current directory is not a Git repository.`);
process.exit(1);
}
const curr = getCurrentBranch(cwd);
if (!curr) {
console.error(`${LOG.error} Could not resolve current branch.`);
process.exit(1);
}
console.log(`${LOG.info} Rebase mode: parent`);
const base = getParentFromJson(curr);
if (!base) {
console.error(`${LOG.error} Parent branch not found for "${curr}" in branches.json.`);
console.error(`${LOG.hint} Please add the following to branches.json:`);
console.error(`${LOG.hint} { "${curr}": "<base-branch>" }`);
process.exit(1);
}
console.log(`${LOG.info} Using parent branch from config: "${base}"`);
await fetchRebase(cwd, base);
}
// ---------- Main ----------
(async () => {
const mode = process.argv[2] || 'this';
try {
switch (mode) {
case 'this':
await doThis();
break;
case 'all':
await doAll();
break;
case 'parent':
await doParent();
break;
default:
console.error(`${LOG.error} Invalid rebase mode: "${mode}"`);
console.error(`${LOG.hint} Available modes: this, all, parent`);
process.exit(1);
}
} catch (err) {
// catch 블록으로 넘어온 에러는 이미 출력되었으므로 Exit Code만 설정
process.exit(1);
}
})();