@vibe-validate/git 0.17.6 → 0.18.0

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,592 @@
1
+ /**
2
+ * Branch Cleanup - Core Analysis Functions
3
+ *
4
+ * Git-aware branch cleanup that safely identifies and removes merged branches.
5
+ * This module provides the core analysis functionality for determining which
6
+ * branches are safe to delete automatically and which need manual review.
7
+ *
8
+ * Safety principles:
9
+ * 1. NEVER delete branches with unpushed work
10
+ * 2. Auto-delete only if 100% safe (merged + no unpushed commits)
11
+ * 3. All deletions are recoverable via reflog
12
+ * 4. Never touch protected branches (main/master/develop)
13
+ *
14
+ * @packageDocumentation
15
+ */
16
+ import { isToolAvailable } from '@vibe-validate/utils';
17
+ import { listPullRequests, fetchPRDetails } from './gh-commands.js';
18
+ import { getCurrentBranch } from './git-commands.js';
19
+ import { execGitCommand, tryGitCommand } from './git-executor.js';
20
+ /**
21
+ * Detect the repository's default branch
22
+ *
23
+ * Tries multiple methods in order of reliability:
24
+ * 1. Remote HEAD symbolic ref (most reliable)
25
+ * 2. Git config init.defaultBranch
26
+ * 3. Fallback to 'main'
27
+ *
28
+ * @param options - Options for detection
29
+ * @returns Default branch name
30
+ * @throws Error if throwOnError is true and detection fails
31
+ */
32
+ export function detectDefaultBranch(options = {}) {
33
+ const { throwOnError = false } = options;
34
+ try {
35
+ // Try remote HEAD symbolic ref first (most reliable)
36
+ const symbolicRef = execGitCommand(['symbolic-ref', 'refs/remotes/origin/HEAD']);
37
+ const regex = /refs\/heads\/(.+)$/;
38
+ const match = regex.exec(symbolicRef);
39
+ if (match) {
40
+ return match[1];
41
+ }
42
+ }
43
+ catch {
44
+ // Fall through to next method
45
+ }
46
+ try {
47
+ // Try git config
48
+ const configBranch = execGitCommand(['config', '--get', 'init.defaultBranch']);
49
+ if (configBranch) {
50
+ // Remove refs/heads/ prefix if present
51
+ return configBranch.replace(/^refs\/heads\//, '');
52
+ }
53
+ }
54
+ catch {
55
+ // Fall through to fallback
56
+ }
57
+ // Check if we should throw or use fallback
58
+ if (throwOnError && !tryGitCommand(['rev-parse', '--verify', 'main'])) {
59
+ throw new Error('Unable to detect default branch');
60
+ }
61
+ // Last resort fallback
62
+ return 'main';
63
+ }
64
+ /**
65
+ * Check if a branch is protected (should never be deleted)
66
+ *
67
+ * @param name - Branch name
68
+ * @param defaultBranch - Default branch name
69
+ * @returns true if branch is protected
70
+ */
71
+ export function isProtectedBranch(name, defaultBranch) {
72
+ const protectedBranches = ['main', 'master', 'develop', defaultBranch];
73
+ return protectedBranches.includes(name);
74
+ }
75
+ /**
76
+ * Parse remote tracking status from git branch -vv output
77
+ *
78
+ * @param branchVerbose - Output from git branch -vv
79
+ * @returns Remote status and ref
80
+ */
81
+ export function parseRemoteTracking(branchVerbose) {
82
+ // Extract tracking ref from git branch -vv format
83
+ // Format: "* branch-name abc1234 [origin/branch-name: ahead 2] Commit message"
84
+ // Use non-backtracking character class to avoid catastrophic backtracking
85
+ // eslint-disable-next-line sonarjs/slow-regex
86
+ const trackingRegex = /\[([^\]]*)\]/;
87
+ const trackingMatch = trackingRegex.exec(branchVerbose);
88
+ if (!trackingMatch) {
89
+ return { remoteStatus: 'never_pushed', remoteRef: null };
90
+ }
91
+ const trackingInfo = trackingMatch[1];
92
+ const remoteRef = trackingInfo.split(':')[0].trim();
93
+ return { remoteStatus: 'exists', remoteRef };
94
+ }
95
+ /**
96
+ * Get unpushed commit count for a branch
97
+ *
98
+ * @param branch - Branch name
99
+ * @param remoteRef - Remote ref (e.g., 'origin/feature/test')
100
+ * @returns Number of unpushed commits
101
+ */
102
+ export function getUnpushedCommitCount(branch, remoteRef) {
103
+ if (!remoteRef) {
104
+ return 0;
105
+ }
106
+ try {
107
+ const count = execGitCommand(['rev-list', '--count', `${remoteRef}..${branch}`]);
108
+ return Number.parseInt(count, 10) || 0;
109
+ }
110
+ catch {
111
+ return 0;
112
+ }
113
+ }
114
+ /**
115
+ * Gather git facts about a branch
116
+ *
117
+ * @param branch - Branch name
118
+ * @param _defaultBranch - Default branch name (reserved for future use)
119
+ * @param mergedBranches - Set of branch names that are merged to main
120
+ * @returns Git facts about the branch
121
+ */
122
+ export async function gatherBranchGitFacts(branch, _defaultBranch, mergedBranches) {
123
+ // Check if merged to main
124
+ const mergedToMain = mergedBranches.has(branch);
125
+ // Get remote tracking status
126
+ let remoteStatus = 'never_pushed';
127
+ let remoteRef = null;
128
+ try {
129
+ // Check for tracking ref
130
+ const trackingRef = execGitCommand(['config', '--get', `branch.${branch}.merge`]);
131
+ if (trackingRef) {
132
+ // Extract branch name from refs/heads/xxx
133
+ const remoteBranch = trackingRef.replace('refs/heads/', '');
134
+ const remote = execGitCommand(['config', '--get', `branch.${branch}.remote`]) || 'origin';
135
+ remoteRef = `${remote}/${remoteBranch}`;
136
+ // Check if remote ref exists
137
+ try {
138
+ execGitCommand(['rev-parse', '--verify', `refs/remotes/${remoteRef}`]);
139
+ remoteStatus = 'exists';
140
+ }
141
+ catch {
142
+ remoteStatus = 'deleted';
143
+ }
144
+ }
145
+ }
146
+ catch {
147
+ remoteStatus = 'never_pushed';
148
+ }
149
+ // Get unpushed commit count
150
+ const unpushedCommitCount = getUnpushedCommitCount(branch, remoteRef);
151
+ // Get commit info
152
+ const commitInfo = execGitCommand([
153
+ 'log',
154
+ '-1',
155
+ '--format=%aI%n%an',
156
+ branch,
157
+ ]);
158
+ const [lastCommitDate, lastCommitAuthor] = commitInfo.split('\n');
159
+ // Calculate days since activity
160
+ const commitDate = new Date(lastCommitDate);
161
+ const now = new Date();
162
+ const daysSinceActivity = Math.floor((now.getTime() - commitDate.getTime()) / (1000 * 60 * 60 * 24));
163
+ return {
164
+ name: branch,
165
+ mergedToMain,
166
+ remoteStatus,
167
+ unpushedCommitCount,
168
+ lastCommitDate,
169
+ lastCommitAuthor,
170
+ daysSinceActivity,
171
+ };
172
+ }
173
+ /**
174
+ * Determine if a branch is 100% safe to auto-delete
175
+ *
176
+ * A branch is auto-delete safe if:
177
+ * - Merged to main (detected by git)
178
+ * - Zero unpushed commits
179
+ * - Even if deleted, it's recoverable via reflog
180
+ *
181
+ * @param facts - Git facts about the branch
182
+ * @returns true if safe to auto-delete
183
+ */
184
+ export function isAutoDeleteSafe(facts) {
185
+ // Must be merged to main
186
+ if (!facts.mergedToMain) {
187
+ return false;
188
+ }
189
+ // Must have no unpushed work
190
+ if (facts.unpushedCommitCount > 0) {
191
+ return false;
192
+ }
193
+ return true;
194
+ }
195
+ /**
196
+ * Determine if a branch needs manual review before deletion
197
+ *
198
+ * A branch needs review if:
199
+ * - Not merged (could be squash/rebase merged)
200
+ * - Zero unpushed commits (safe from data loss)
201
+ * - Remote deleted OR >30 days old
202
+ * - NOT if has open PR (obviously keep)
203
+ * - NOT if has unpushed work (obviously keep)
204
+ *
205
+ * @param facts - Git facts about the branch
206
+ * @param githubFacts - GitHub facts (used to check PR state)
207
+ * @returns true if needs manual review
208
+ */
209
+ export function needsReview(facts, githubFacts) {
210
+ // Never review branches with unpushed work
211
+ if (facts.unpushedCommitCount > 0) {
212
+ return false;
213
+ }
214
+ // Never review if PR is still open (obviously keep)
215
+ if (githubFacts?.prState === 'open') {
216
+ return false;
217
+ }
218
+ // Never review active branches (< 30 days)
219
+ if (facts.daysSinceActivity < 30) {
220
+ return false;
221
+ }
222
+ // Don't review if already auto-delete safe
223
+ if (facts.mergedToMain) {
224
+ return false;
225
+ }
226
+ // Review if PR was merged and remote deleted (squash merge pattern)
227
+ if (githubFacts?.prState === 'merged' && facts.remoteStatus === 'deleted') {
228
+ return true;
229
+ }
230
+ // Review if remote deleted (likely squash merged, even without PR data)
231
+ if (facts.remoteStatus === 'deleted') {
232
+ return true;
233
+ }
234
+ // Review if old and never pushed (abandoned local work)
235
+ if (facts.remoteStatus === 'never_pushed' && facts.daysSinceActivity >= 90) {
236
+ return true;
237
+ }
238
+ return false;
239
+ }
240
+ /**
241
+ * Determine if a branch should be shown in output
242
+ *
243
+ * @param analysis - Branch analysis
244
+ * @returns true if should be shown
245
+ */
246
+ export function shouldShowBranch(analysis) {
247
+ // Never show branches with unpushed work
248
+ if (analysis.gitFacts.unpushedCommitCount > 0) {
249
+ return false;
250
+ }
251
+ // Show if auto-delete safe or needs review
252
+ return isAutoDeleteSafe(analysis.gitFacts) || needsReview(analysis.gitFacts, analysis.githubFacts);
253
+ }
254
+ /**
255
+ * Detect merge method from PR data
256
+ *
257
+ * Analyzes PR metadata to determine how it was merged:
258
+ * - Squash: merge commit exists but PR has multiple commits
259
+ * - Merge: merge commit with multiple parents (true merge)
260
+ * - Rebase: commits were rebased onto target branch
261
+ *
262
+ * @param pr - Pull request data
263
+ * @returns Merge method or undefined if cannot determine
264
+ */
265
+ export function detectMergeMethod(pr) {
266
+ // If no merge commit, can't determine
267
+ if (!pr.mergeCommit?.oid) {
268
+ return undefined;
269
+ }
270
+ // Squash merge: merge commit exists but PR had multiple commits
271
+ // The merge commit will have only 1 parent (squashed into single commit)
272
+ if (pr.commits?.totalCount && pr.commits.totalCount > 1 && pr.mergeCommit.parents?.[0]?.totalCount === 1) {
273
+ return 'squash';
274
+ }
275
+ // True merge: merge commit has 2+ parents
276
+ if (pr.mergeCommit.parents?.[0]?.totalCount && pr.mergeCommit.parents[0].totalCount >= 2) {
277
+ return 'merge';
278
+ }
279
+ // Rebase merge: merge commit exists but is not a true merge commit
280
+ // This is harder to detect reliably, so we use this as fallback
281
+ return 'rebase';
282
+ }
283
+ /**
284
+ * Fetch PR data for a list of branches
285
+ *
286
+ * Efficiently batch-fetches merged PRs from GitHub and creates a map
287
+ * of branch name → PR data for quick lookups during enrichment.
288
+ *
289
+ * @param repository - Repository in owner/repo format
290
+ * @param _branches - Branch names (reserved for future filtering)
291
+ * @returns Map of branch name → PR data
292
+ * @throws Error if gh CLI is not available
293
+ */
294
+ export async function fetchPRDataForBranches(repository, _branches) {
295
+ // Check gh CLI availability (hard requirement)
296
+ if (!isToolAvailable('gh')) {
297
+ throw new Error('GitHub CLI (gh) is required for branch cleanup. Install: https://cli.github.com/');
298
+ }
299
+ const prMap = new Map();
300
+ try {
301
+ // Parse repository
302
+ const [owner, repo] = repository.split('/');
303
+ if (!owner || !repo) {
304
+ throw new Error(`Invalid repository format: ${repository}. Expected: owner/repo`);
305
+ }
306
+ // Fetch recent merged PRs (batch operation)
307
+ // Note: 'commits' field excluded to avoid GitHub GraphQL node limit (would exceed 500k nodes for 100 PRs)
308
+ // We fetch commits per-PR in the next step via fetchPRDetails()
309
+ const mergedPRs = listPullRequests(owner, repo, 20, // Last 20 merged PRs covers most cleanup scenarios (most repos have <20 local branches)
310
+ ['number', 'title', 'headRefName', 'baseRefName', 'state', 'mergedAt', 'mergedBy'], 'merged');
311
+ // Process PRs in parallel batches with early termination
312
+ // Stop once we've found PRs for all local branches
313
+ const localBranchSet = new Set(_branches);
314
+ const BATCH_SIZE = 5; // Parallel requests per batch
315
+ for (let i = 0; i < mergedPRs.length; i += BATCH_SIZE) {
316
+ // Early termination: stop if we've found all local branches
317
+ if (prMap.size >= localBranchSet.size) {
318
+ break;
319
+ }
320
+ // Process batch in parallel
321
+ const batch = mergedPRs.slice(i, i + BATCH_SIZE);
322
+ const results = await Promise.allSettled(batch.map(async (pr) => fetchPRDetails(pr.number, owner, repo, [
323
+ 'number',
324
+ 'headRefName',
325
+ 'state',
326
+ 'mergedAt',
327
+ 'mergedBy',
328
+ 'mergeCommit',
329
+ 'commits',
330
+ ])));
331
+ // Add results to map
332
+ for (const [index, result] of results.entries()) {
333
+ const pr = batch[index];
334
+ if (result.status === 'fulfilled') {
335
+ prMap.set(result.value.headRefName, result.value);
336
+ }
337
+ else {
338
+ // If individual PR fetch fails, use basic data
339
+ prMap.set(pr.headRefName, pr);
340
+ }
341
+ }
342
+ }
343
+ return prMap;
344
+ }
345
+ catch (error) {
346
+ // Re-throw with context
347
+ if (error instanceof Error) {
348
+ throw new Error(`Failed to fetch PR data: ${error.message}`);
349
+ }
350
+ throw error;
351
+ }
352
+ }
353
+ /**
354
+ * Enrich branch analyses with GitHub PR data
355
+ *
356
+ * Populates the githubFacts field in each analysis by matching branches
357
+ * to merged PRs. This helps identify squash-merged branches that git
358
+ * doesn't recognize as merged.
359
+ *
360
+ * @param analyses - Branch analyses to enrich
361
+ * @param repository - Repository in owner/repo format
362
+ * @throws Error if gh CLI is not available
363
+ */
364
+ export async function enrichWithGitHubData(analyses, repository) {
365
+ // Extract branch names
366
+ const branchNames = analyses.map(a => a.gitFacts.name);
367
+ // Fetch PR data (throws if gh not available)
368
+ const prMap = await fetchPRDataForBranches(repository, branchNames);
369
+ // Enrich each analysis
370
+ for (const analysis of analyses) {
371
+ const pr = prMap.get(analysis.gitFacts.name);
372
+ if (pr) {
373
+ // Normalize state to lowercase for consistency
374
+ const prState = pr.state?.toLowerCase();
375
+ analysis.githubFacts = {
376
+ prNumber: pr.number,
377
+ prState,
378
+ mergeMethod: detectMergeMethod(pr),
379
+ mergedAt: pr.mergedAt,
380
+ mergedBy: pr.mergedBy?.login,
381
+ };
382
+ }
383
+ }
384
+ }
385
+ /**
386
+ * Setup cleanup context and handle current branch switching
387
+ *
388
+ * If we're on a branch that needs cleanup, switch to default branch first.
389
+ *
390
+ * @returns Cleanup context
391
+ */
392
+ export async function setupCleanupContext() {
393
+ const currentBranch = getCurrentBranch();
394
+ const defaultBranch = detectDefaultBranch();
395
+ const remote = 'origin'; // Currently hardcoded, will be configurable in future
396
+ // Get repository name
397
+ let repository = 'unknown/unknown';
398
+ try {
399
+ const remoteUrl = execGitCommand(['remote', 'get-url', remote]);
400
+ // Match owner/repo pattern from SSH (git@github.com:owner/repo) or HTTPS (https://github.com/owner/repo)
401
+ // Match after last ':' or after last '/' in the path
402
+ const repoRegex = /[:/]([^/:]+\/[^/]+?)(?:\.git)?$/;
403
+ const match = repoRegex.exec(remoteUrl);
404
+ if (match) {
405
+ repository = match[1];
406
+ }
407
+ }
408
+ catch {
409
+ // Keep default
410
+ }
411
+ const context = {
412
+ repository,
413
+ remote,
414
+ defaultBranch,
415
+ currentBranch,
416
+ switchedBranch: false,
417
+ };
418
+ // If already on default branch, nothing to do
419
+ if (currentBranch === defaultBranch) {
420
+ return context;
421
+ }
422
+ // Check if current branch needs cleanup
423
+ const mergedBranches = new Set(execGitCommand(['branch', '--merged', defaultBranch])
424
+ .split('\n')
425
+ .map(b => b.trim().replace(/^\*\s+/, ''))
426
+ .filter(Boolean));
427
+ const currentBranchFacts = await gatherBranchGitFacts(currentBranch, defaultBranch, mergedBranches);
428
+ // Decide if we need to switch
429
+ let shouldSwitch = false;
430
+ let switchReason;
431
+ if (isAutoDeleteSafe(currentBranchFacts)) {
432
+ shouldSwitch = true;
433
+ switchReason = 'current branch is auto-delete safe';
434
+ }
435
+ else if (needsReview(currentBranchFacts)) {
436
+ shouldSwitch = true;
437
+ switchReason = 'current branch needs review';
438
+ }
439
+ if (shouldSwitch) {
440
+ // Switch to default branch
441
+ execGitCommand(['checkout', defaultBranch]);
442
+ context.previousBranch = currentBranch;
443
+ context.currentBranch = defaultBranch;
444
+ context.switchedBranch = true;
445
+ context.switchReason = switchReason;
446
+ }
447
+ return context;
448
+ }
449
+ /**
450
+ * Generate assessment text for a branch needing review
451
+ */
452
+ export function generateAssessment(gitFacts, githubFacts) {
453
+ if (gitFacts.remoteStatus === 'deleted') {
454
+ return generateDeletedRemoteAssessment(gitFacts, githubFacts);
455
+ }
456
+ if (gitFacts.remoteStatus === 'never_pushed' && gitFacts.daysSinceActivity >= 90) {
457
+ return `Old abandoned branch (${gitFacts.daysSinceActivity} days)\nNever pushed to remote\nNo unpushed commits (safe to delete)`;
458
+ }
459
+ return 'Needs manual review';
460
+ }
461
+ /**
462
+ * Generate assessment for branch with deleted remote
463
+ */
464
+ export function generateDeletedRemoteAssessment(gitFacts, githubFacts) {
465
+ let text = 'Remote deleted by GitHub\n';
466
+ if (githubFacts?.prNumber) {
467
+ text += `PR #${githubFacts.prNumber} ${githubFacts.prState} ${gitFacts.daysSinceActivity} days ago`;
468
+ if (githubFacts.mergedBy) {
469
+ text += ` by ${githubFacts.mergedBy}`;
470
+ }
471
+ text += '\n';
472
+ if (githubFacts.mergeMethod) {
473
+ text += `${githubFacts.mergeMethod} merge explains why git branch --merged returned false\n`;
474
+ }
475
+ }
476
+ text += 'No unpushed commits (safe to delete)';
477
+ return text;
478
+ }
479
+ /**
480
+ * Try to delete a safe branch and return result
481
+ */
482
+ export function tryDeleteBranch(gitFacts) {
483
+ try {
484
+ execGitCommand(['branch', '-d', gitFacts.name]);
485
+ return { deleted: true };
486
+ }
487
+ catch (error) {
488
+ return {
489
+ deleted: false,
490
+ error: error instanceof Error ? error.message : String(error),
491
+ };
492
+ }
493
+ }
494
+ /**
495
+ * Categorize branches into auto-delete and needs-review
496
+ */
497
+ export function categorizeBranches(analyses) {
498
+ const autoDeleted = [];
499
+ const branchesNeedingReview = [];
500
+ for (const analysis of analyses) {
501
+ const { gitFacts, githubFacts, assessment } = analysis;
502
+ if (isAutoDeleteSafe(gitFacts)) {
503
+ const result = tryDeleteBranch(gitFacts);
504
+ if (result.deleted) {
505
+ autoDeleted.push({
506
+ name: gitFacts.name,
507
+ reason: 'merged_to_main',
508
+ recoveryCommand: assessment.recoveryCommand,
509
+ });
510
+ }
511
+ else {
512
+ branchesNeedingReview.push({
513
+ name: gitFacts.name,
514
+ verification: { ...gitFacts, ...githubFacts },
515
+ assessment: `Failed to delete: ${result.error}`,
516
+ deleteCommand: assessment.deleteCommand,
517
+ recoveryCommand: assessment.recoveryCommand,
518
+ });
519
+ }
520
+ }
521
+ else if (needsReview(gitFacts, githubFacts)) {
522
+ branchesNeedingReview.push({
523
+ name: gitFacts.name,
524
+ verification: { ...gitFacts, ...githubFacts },
525
+ assessment: generateAssessment(gitFacts, githubFacts),
526
+ deleteCommand: assessment.deleteCommand,
527
+ recoveryCommand: assessment.recoveryCommand,
528
+ });
529
+ }
530
+ }
531
+ return { autoDeleted, needsReview: branchesNeedingReview };
532
+ }
533
+ /**
534
+ * Perform comprehensive branch cleanup
535
+ *
536
+ * This is the main entry point for branch cleanup. It:
537
+ * 1. Sets up context and switches branches if needed
538
+ * 2. Gathers all local branches
539
+ * 3. Analyzes each branch (git facts + GitHub enrichment)
540
+ * 4. Categorizes branches (auto-delete vs needs-review)
541
+ * 5. Deletes safe branches
542
+ * 6. Returns structured result with YAML-compatible format
543
+ *
544
+ * @returns Cleanup result with detailed analysis
545
+ */
546
+ export async function cleanupBranches() {
547
+ // Step 1: Setup context (may switch branches)
548
+ const context = await setupCleanupContext();
549
+ // Step 2: Get all local branches (excluding protected)
550
+ const allBranches = execGitCommand(['branch', '--format=%(refname:short)'])
551
+ .split('\n')
552
+ .map(b => b.trim())
553
+ .filter(Boolean)
554
+ .filter(b => !isProtectedBranch(b, context.defaultBranch));
555
+ // Step 3: Get merged branches list (for efficient lookup)
556
+ const mergedBranches = new Set(execGitCommand(['branch', '--merged', context.defaultBranch])
557
+ .split('\n')
558
+ .map(b => b.trim().replace(/^\*\s+/, ''))
559
+ .filter(Boolean));
560
+ // Step 4: Analyze all branches (gather git facts)
561
+ const analyses = [];
562
+ for (const branch of allBranches) {
563
+ const gitFacts = await gatherBranchGitFacts(branch, context.defaultBranch, mergedBranches);
564
+ analyses.push({
565
+ gitFacts,
566
+ assessment: {
567
+ summary: '',
568
+ deleteCommand: `git branch -D ${branch}`,
569
+ recoveryCommand: `git reflog | grep '${branch}' | head -1`,
570
+ },
571
+ });
572
+ }
573
+ // Step 5: Enrich with GitHub data (throws if gh not available)
574
+ await enrichWithGitHubData(analyses, context.repository);
575
+ // Step 6: Categorize and delete safe branches
576
+ const { autoDeleted, needsReview } = categorizeBranches(analyses);
577
+ // Step 7: Build result
578
+ return {
579
+ context,
580
+ autoDeleted,
581
+ needsReview,
582
+ summary: {
583
+ autoDeletedCount: autoDeleted.length,
584
+ needsReviewCount: needsReview.length,
585
+ totalBranchesAnalyzed: analyses.length,
586
+ },
587
+ recoveryInfo: 'Deleted branches are recoverable for 30 days via git reflog:\n' +
588
+ ' git reflog\n' +
589
+ ' git checkout -b <branch-name> <SHA>',
590
+ };
591
+ }
592
+ //# sourceMappingURL=branch-cleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-cleanup.js","sourceRoot":"","sources":["../src/branch-cleanup.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"branch-sync.d.ts","sourceRoot":"","sources":["../src/branch-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAGD,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE3F,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AA4CD;;;;GAIG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;gBAE9B,OAAO,GAAE,gBAAqB;IAK1C;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC;YA0C7B,gBAAgB;YAUhB,eAAe;YAUf,WAAW;YAUX,gBAAgB;IAW9B;;;;;OAKG;IACH,WAAW,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM;CAK7C;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CAG9F"}
1
+ {"version":3,"file":"branch-sync.d.ts","sourceRoot":"","sources":["../src/branch-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAGD,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE3F,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAwBD;;;;GAIG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;gBAE9B,OAAO,GAAE,gBAAqB;IAK1C;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC;YA0C7B,gBAAgB;YAUhB,eAAe;YAUf,WAAW;YAUX,gBAAgB;IAW9B;;;;;OAKG;IACH,WAAW,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM;CAK7C;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CAG9F"}
@@ -10,44 +10,28 @@
10
10
  * - Explicit instructions when manual intervention needed
11
11
  * - Cross-platform compatibility
12
12
  */
13
- import { spawn } from 'node:child_process';
14
- const GIT_TIMEOUT = 30000; // 30 seconds timeout for git operations
13
+ import { executeGitCommand } from './git-executor.js';
15
14
  /**
16
- * Execute git command safely using spawn (prevents command injection)
15
+ * Adapt centralized git-executor to the async GitExecutor interface
16
+ *
17
+ * Uses the centralized git command execution from git-executor.ts,
18
+ * wrapping it in a Promise to maintain compatibility with the async
19
+ * GitExecutor interface used for dependency injection in tests.
17
20
  *
18
21
  * @param args - Git command arguments (e.g., ['rev-parse', '--abbrev-ref', 'HEAD'])
19
22
  * @returns Promise resolving to stdout and stderr
20
23
  */
21
24
  function execGit(args) {
22
- return new Promise((resolve, reject) => {
23
- const child = spawn('git', args, {
24
- timeout: GIT_TIMEOUT,
25
- stdio: ['ignore', 'pipe', 'pipe'], // No stdin (git commands shouldn't need interactive input), capture stdout/stderr
25
+ try {
26
+ const result = executeGitCommand(args, { ignoreErrors: false });
27
+ return Promise.resolve({
28
+ stdout: result.stdout,
29
+ stderr: result.stderr,
26
30
  });
27
- let stdout = '';
28
- let stderr = '';
29
- if (child.stdout) {
30
- child.stdout.on('data', (data) => {
31
- stdout += data.toString();
32
- });
33
- }
34
- if (child.stderr) {
35
- child.stderr.on('data', (data) => {
36
- stderr += data.toString();
37
- });
38
- }
39
- child.on('error', (error) => {
40
- reject(error);
41
- });
42
- child.on('close', (code) => {
43
- if (code === 0) {
44
- resolve({ stdout, stderr });
45
- }
46
- else {
47
- reject(new Error(`git exited with code ${code}: ${stderr}`));
48
- }
49
- });
50
- });
31
+ }
32
+ catch (error) {
33
+ return Promise.reject(error);
34
+ }
51
35
  }
52
36
  /**
53
37
  * Branch Sync Checker