@akiojin/gwt 4.3.1 → 4.4.1
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/cli/ui/components/App.d.ts.map +1 -1
- package/dist/cli/ui/components/App.js +12 -61
- package/dist/cli/ui/components/App.js.map +1 -1
- package/dist/cli/ui/components/common/SpinnerIcon.d.ts +20 -0
- package/dist/cli/ui/components/common/SpinnerIcon.d.ts.map +1 -0
- package/dist/cli/ui/components/common/SpinnerIcon.js +61 -0
- package/dist/cli/ui/components/common/SpinnerIcon.js.map +1 -0
- package/dist/cli/ui/components/parts/Stats.d.ts +2 -5
- package/dist/cli/ui/components/parts/Stats.d.ts.map +1 -1
- package/dist/cli/ui/components/parts/Stats.js +16 -3
- package/dist/cli/ui/components/parts/Stats.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts +6 -2
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.js +95 -42
- package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
- package/dist/cli/ui/hooks/useAppInput.d.ts +1 -0
- package/dist/cli/ui/hooks/useAppInput.d.ts.map +1 -1
- package/dist/cli/ui/hooks/useAppInput.js +2 -1
- package/dist/cli/ui/hooks/useAppInput.js.map +1 -1
- package/dist/cli/ui/hooks/useGitData.d.ts +1 -0
- package/dist/cli/ui/hooks/useGitData.d.ts.map +1 -1
- package/dist/cli/ui/hooks/useGitData.js +43 -15
- package/dist/cli/ui/hooks/useGitData.js.map +1 -1
- package/dist/cli/ui/types.d.ts +4 -0
- package/dist/cli/ui/types.d.ts.map +1 -1
- package/dist/git.d.ts +7 -4
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +54 -34
- package/dist/git.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +208 -0
- package/src/cli/ui/__tests__/hooks/useGitData.nonblocking.test.tsx +206 -0
- package/src/cli/ui/components/App.tsx +22 -77
- package/src/cli/ui/components/common/SpinnerIcon.tsx +86 -0
- package/src/cli/ui/components/parts/Stats.tsx +24 -3
- package/src/cli/ui/components/screens/BranchListScreen.tsx +117 -45
- package/src/cli/ui/hooks/useAppInput.ts +2 -1
- package/src/cli/ui/hooks/useGitData.ts +101 -18
- package/src/cli/ui/screens/__tests__/BranchActionSelectorScreen.test.tsx +46 -1
- package/src/cli/ui/types.ts +5 -0
- package/src/git.ts +72 -37
- package/src/index.ts +14 -1
package/src/git.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execa } from "execa";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { BranchInfo } from "./cli/ui/types.js";
|
|
4
|
+
import { GIT_CONFIG } from "./config/constants.js";
|
|
4
5
|
|
|
5
6
|
export class GitError extends Error {
|
|
6
7
|
constructor(
|
|
@@ -40,12 +41,17 @@ export async function isGitRepository(): Promise<boolean> {
|
|
|
40
41
|
console.error(`[DEBUG] git rev-parse --git-dir: ${result.stdout}`);
|
|
41
42
|
}
|
|
42
43
|
return true;
|
|
43
|
-
} catch (error:
|
|
44
|
+
} catch (error: unknown) {
|
|
44
45
|
// Debug: log the error for troubleshooting
|
|
45
46
|
if (process.env.DEBUG) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
48
|
+
const stderr =
|
|
49
|
+
typeof error === "object" && error !== null && "stderr" in error
|
|
50
|
+
? (error as { stderr?: string }).stderr
|
|
51
|
+
: undefined;
|
|
52
|
+
console.error(`[DEBUG] git rev-parse --git-dir failed:`, message);
|
|
53
|
+
if (stderr) {
|
|
54
|
+
console.error(`[DEBUG] stderr:`, stderr);
|
|
49
55
|
}
|
|
50
56
|
}
|
|
51
57
|
return false;
|
|
@@ -171,11 +177,11 @@ export async function getWorktreeRoot(): Promise<string> {
|
|
|
171
177
|
}
|
|
172
178
|
}
|
|
173
179
|
|
|
174
|
-
export async function getCurrentBranch(): Promise<string | null> {
|
|
180
|
+
export async function getCurrentBranch(cwd?: string): Promise<string | null> {
|
|
175
181
|
try {
|
|
176
|
-
const
|
|
182
|
+
const workDir = cwd ?? (await getRepositoryRoot());
|
|
177
183
|
const { stdout } = await execa("git", ["branch", "--show-current"], {
|
|
178
|
-
cwd:
|
|
184
|
+
cwd: workDir,
|
|
179
185
|
});
|
|
180
186
|
return stdout.trim() || null;
|
|
181
187
|
} catch {
|
|
@@ -221,13 +227,15 @@ async function getBranchCommitTimestamps(
|
|
|
221
227
|
}
|
|
222
228
|
}
|
|
223
229
|
|
|
224
|
-
export async function getLocalBranches(): Promise<BranchInfo[]> {
|
|
230
|
+
export async function getLocalBranches(cwd?: string): Promise<BranchInfo[]> {
|
|
225
231
|
try {
|
|
226
|
-
const
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
"
|
|
230
|
-
|
|
232
|
+
const execOptions = cwd ? { cwd } : undefined;
|
|
233
|
+
const commitMap = await getBranchCommitTimestamps(["refs/heads"], cwd);
|
|
234
|
+
const { stdout = "" } = (await execa(
|
|
235
|
+
"git",
|
|
236
|
+
["branch", "--format=%(refname:short)"],
|
|
237
|
+
execOptions,
|
|
238
|
+
)) ?? { stdout: "" };
|
|
231
239
|
return stdout
|
|
232
240
|
.split("\n")
|
|
233
241
|
.filter((line) => line.trim())
|
|
@@ -250,14 +258,15 @@ export async function getLocalBranches(): Promise<BranchInfo[]> {
|
|
|
250
258
|
}
|
|
251
259
|
}
|
|
252
260
|
|
|
253
|
-
export async function getRemoteBranches(): Promise<BranchInfo[]> {
|
|
261
|
+
export async function getRemoteBranches(cwd?: string): Promise<BranchInfo[]> {
|
|
254
262
|
try {
|
|
255
|
-
const
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
"
|
|
259
|
-
"--format=%(refname:short)",
|
|
260
|
-
|
|
263
|
+
const execOptions = cwd ? { cwd } : undefined;
|
|
264
|
+
const commitMap = await getBranchCommitTimestamps(["refs/remotes"], cwd);
|
|
265
|
+
const { stdout = "" } = (await execa(
|
|
266
|
+
"git",
|
|
267
|
+
["branch", "-r", "--format=%(refname:short)"],
|
|
268
|
+
execOptions,
|
|
269
|
+
)) ?? { stdout: "" };
|
|
261
270
|
return stdout
|
|
262
271
|
.split("\n")
|
|
263
272
|
.filter((line) => line.trim() && !line.includes("HEAD"))
|
|
@@ -283,13 +292,14 @@ export async function getRemoteBranches(): Promise<BranchInfo[]> {
|
|
|
283
292
|
|
|
284
293
|
/**
|
|
285
294
|
* ローカルとリモートのすべてのブランチ情報を取得
|
|
295
|
+
* @param cwd - 作業ディレクトリ(省略時はリポジトリルート)
|
|
286
296
|
* @returns {Promise<BranchInfo[]>} ブランチ情報の配列
|
|
287
297
|
*/
|
|
288
|
-
export async function getAllBranches(): Promise<BranchInfo[]> {
|
|
298
|
+
export async function getAllBranches(cwd?: string): Promise<BranchInfo[]> {
|
|
289
299
|
const [localBranches, remoteBranches, currentBranch] = await Promise.all([
|
|
290
|
-
getLocalBranches(),
|
|
291
|
-
getRemoteBranches(),
|
|
292
|
-
getCurrentBranch(),
|
|
300
|
+
getLocalBranches(cwd),
|
|
301
|
+
getRemoteBranches(cwd),
|
|
302
|
+
getCurrentBranch(cwd),
|
|
293
303
|
]);
|
|
294
304
|
|
|
295
305
|
// 現在のブランチ情報を設定
|
|
@@ -752,15 +762,27 @@ export async function getEnhancedSessionInfo(
|
|
|
752
762
|
|
|
753
763
|
export async function fetchAllRemotes(options?: {
|
|
754
764
|
cwd?: string;
|
|
765
|
+
timeoutMs?: number;
|
|
766
|
+
allowPrompt?: boolean;
|
|
755
767
|
}): Promise<void> {
|
|
756
768
|
try {
|
|
757
|
-
const execOptions = options?.cwd ? { cwd: options.cwd } :
|
|
769
|
+
const execOptions = options?.cwd ? { cwd: options.cwd } : {};
|
|
770
|
+
const timeoutMs = options?.timeoutMs ?? GIT_CONFIG.FETCH_TIMEOUT;
|
|
771
|
+
const allowPrompt = options?.allowPrompt === true;
|
|
772
|
+
const env = allowPrompt
|
|
773
|
+
? process.env
|
|
774
|
+
: {
|
|
775
|
+
...process.env,
|
|
776
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
777
|
+
GCM_INTERACTIVE: "Never",
|
|
778
|
+
};
|
|
758
779
|
const args = ["fetch", "--all", "--prune"];
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
780
|
+
await execa("git", args, {
|
|
781
|
+
...execOptions,
|
|
782
|
+
timeout: timeoutMs,
|
|
783
|
+
env,
|
|
784
|
+
stdin: allowPrompt ? "inherit" : "ignore",
|
|
785
|
+
});
|
|
764
786
|
} catch (error) {
|
|
765
787
|
throw new GitError("Failed to fetch remote branches", error);
|
|
766
788
|
}
|
|
@@ -854,11 +876,19 @@ export async function executeNpmVersionInWorktree(
|
|
|
854
876
|
{ cwd: worktreePath },
|
|
855
877
|
);
|
|
856
878
|
}
|
|
857
|
-
} catch (error:
|
|
879
|
+
} catch (error: unknown) {
|
|
858
880
|
// エラーの詳細情報を含める
|
|
859
881
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
860
|
-
const
|
|
861
|
-
|
|
882
|
+
const stderr =
|
|
883
|
+
typeof error === "object" && error !== null && "stderr" in error
|
|
884
|
+
? (error as { stderr?: string }).stderr
|
|
885
|
+
: undefined;
|
|
886
|
+
const stdout =
|
|
887
|
+
typeof error === "object" && error !== null && "stdout" in error
|
|
888
|
+
? (error as { stdout?: string }).stdout
|
|
889
|
+
: undefined;
|
|
890
|
+
const errorDetails = stderr ? ` (stderr: ${stderr})` : "";
|
|
891
|
+
const errorStdout = stdout ? ` (stdout: ${stdout})` : "";
|
|
862
892
|
throw new GitError(
|
|
863
893
|
`Failed to update version to ${newVersion} in worktree: ${errorMessage}${errorDetails}${errorStdout}`,
|
|
864
894
|
error,
|
|
@@ -1201,9 +1231,13 @@ export async function ensureGitignoreEntry(
|
|
|
1201
1231
|
if (content.includes("\r\n")) {
|
|
1202
1232
|
eol = "\r\n";
|
|
1203
1233
|
}
|
|
1204
|
-
} catch (error:
|
|
1234
|
+
} catch (error: unknown) {
|
|
1205
1235
|
// ENOENTエラー(ファイルが存在しない)は無視
|
|
1206
|
-
|
|
1236
|
+
const code =
|
|
1237
|
+
typeof error === "object" && error !== null && "code" in error
|
|
1238
|
+
? (error as { code?: string }).code
|
|
1239
|
+
: undefined;
|
|
1240
|
+
if (code !== "ENOENT") {
|
|
1207
1241
|
throw error;
|
|
1208
1242
|
}
|
|
1209
1243
|
}
|
|
@@ -1222,7 +1256,8 @@ export async function ensureGitignoreEntry(
|
|
|
1222
1256
|
|
|
1223
1257
|
const newContent = `${content}${separator}${entry}${eol}`;
|
|
1224
1258
|
await fs.writeFile(gitignorePath, newContent, "utf-8");
|
|
1225
|
-
} catch (error:
|
|
1226
|
-
|
|
1259
|
+
} catch (error: unknown) {
|
|
1260
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1261
|
+
throw new GitError(`Failed to update .gitignore: ${message}`, error);
|
|
1227
1262
|
}
|
|
1228
1263
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
3
5
|
import {
|
|
4
6
|
isGitRepository,
|
|
5
7
|
getRepositoryRoot,
|
|
@@ -914,8 +916,19 @@ export async function main(): Promise<void> {
|
|
|
914
916
|
await runInteractiveLoop();
|
|
915
917
|
}
|
|
916
918
|
|
|
919
|
+
export function isEntryPoint(metaUrl: string, argv1?: string): boolean {
|
|
920
|
+
if (!argv1) {
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
923
|
+
try {
|
|
924
|
+
return fileURLToPath(metaUrl) === path.resolve(argv1);
|
|
925
|
+
} catch {
|
|
926
|
+
return false;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
917
930
|
// Run the application if this module is executed directly
|
|
918
|
-
if (import.meta.url
|
|
931
|
+
if (isEntryPoint(import.meta.url, process.argv[1])) {
|
|
919
932
|
main().catch(async (error) => {
|
|
920
933
|
console.error("Fatal error:", error);
|
|
921
934
|
await waitForErrorAcknowledgement();
|