| ... | @@ -415,6 +415,12 @@ function isMissingGitCwdError(error: GitCommandError): boolean { | ... | @@ -415,6 +415,12 @@ function isMissingGitCwdError(error: GitCommandError): boolean { |
| 415 | function isNonRepositoryGitStderr(stderr: string): boolean { | 415 | function isNonRepositoryGitStderr(stderr: string): boolean { |
| 416 | return stderr.toLowerCase().includes("not a git repository"); | 416 | return stderr.toLowerCase().includes("not a git repository"); |
| 417 | } | 417 | } |
| | 418 | function isUnbornHeadStderr(stderr: string): boolean { |
| | 419 | return ( |
| | 420 | stderr.toLowerCase().includes("unknown revision") && |
| | 421 | stderr.toLowerCase().includes("path not in the working tree") |
| | 422 | ); |
| | 423 | } |
| 418 | | 424 | |
| 419 | interface Trace2Monitor { | 425 | interface Trace2Monitor { |
| 420 | readonly env: NodeJS.ProcessEnv; | 426 | readonly env: NodeJS.ProcessEnv; |
| ... | @@ -1511,27 +1517,73 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* | ... | @@ -1511,27 +1517,73 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* |
| 1511 | }); | 1517 | }); |
| 1512 | } | 1518 | } |
| 1513 | | 1519 | |
| 1514 | const [unstagedNumstatStdout, stagedNumstatStdout, defaultRefResult, hasPrimaryRemote] = | 1520 | const [numstatStdout, defaultRefResult, hasPrimaryRemote] = yield* Effect.all( |
| 1515 | yield* Effect.all( | 1521 | [ |
| 1516 | [ | 1522 | executeGitWithStableDiagnostics( |
| 1517 | runGitStdout("GitVcsDriver.statusDetails.unstagedNumstat", cwd, ["diff", "--numstat"]), | 1523 | "GitVcsDriver.statusDetails.numstat", |
| 1518 | runGitStdout("GitVcsDriver.statusDetails.stagedNumstat", cwd, [ | 1524 | cwd, |
| 1519 | "diff", | 1525 | ["diff", "HEAD", "--numstat"], |
| 1520 | "--cached", | 1526 | { allowNonZeroExit: true }, |
| 1521 | "--numstat", | 1527 | ).pipe( |
| 1522 | ]), | 1528 | Effect.flatMap((result) => { |
| 1523 | executeGit( | 1529 | if (result.exitCode === 0) return Effect.succeed(result.stdout); |
| 1524 | "GitVcsDriver.statusDetails.defaultRef", | 1530 | if (isUnbornHeadStderr(result.stderr)) { |
| 1525 | cwd, | 1531 | return Effect.map( |
| 1526 | ["symbolic-ref", "refs/remotes/origin/HEAD"], | 1532 | Effect.all([ |
| 1527 | { | 1533 | runGitStdout("GitVcsDriver.statusDetails.numstat.unborn", cwd, [ |
| 1528 | allowNonZeroExit: true, | 1534 | "diff", |
| 1529 | }, | 1535 | "--numstat", |
| 1530 | ), | 1536 | ]), |
| 1531 | originRemoteExists(cwd).pipe(Effect.orElseSucceed(() => false)), | 1537 | runGitStdout("GitVcsDriver.statusDetails.numstat.unborn.staged", cwd, [ |
| 1532 | ], | 1538 | "diff", |
| 1533 | { concurrency: "unbounded" }, | 1539 | "--cached", |
| 1534 | ); | 1540 | "--numstat", |
| | 1541 | ]), |
| | 1542 | ]), |
| | 1543 | ([unstagedStdout, stagedStdout]) => { |
| | 1544 | const staged = parseNumstatEntries(stagedStdout); |
| | 1545 | const unstaged = parseNumstatEntries(unstagedStdout); |
| | 1546 | const map = new Map<string, { insertions: number; deletions: number }>(); |
| | 1547 | for (const entry of [...staged, ...unstaged]) { |
| | 1548 | const existing = map.get(entry.path) ?? { |
| | 1549 | insertions: 0, |
| | 1550 | deletions: 0, |
| | 1551 | }; |
| | 1552 | existing.insertions += entry.insertions; |
| | 1553 | existing.deletions += entry.deletions; |
| | 1554 | map.set(entry.path, existing); |
| | 1555 | } |
| | 1556 | return Array.from(map.entries()) |
| | 1557 | .map(([p, s]) => `${s.insertions}\t${s.deletions}\t${p}`) |
| | 1558 | .join("\n"); |
| | 1559 | }, |
| | 1560 | ); |
| | 1561 | } |
| | 1562 | return Effect.fail( |
| | 1563 | new GitCommandError({ |
| | 1564 | ...gitCommandContext({ |
| | 1565 | operation: "GitVcsDriver.statusDetails.numstat", |
| | 1566 | cwd, |
| | 1567 | args: ["diff", "HEAD", "--numstat"], |
| | 1568 | }), |
| | 1569 | detail: "git diff HEAD --numstat failed.", |
| | 1570 | exitCode: result.exitCode, |
| | 1571 | stdoutLength: result.stdout.length, |
| | 1572 | stderrLength: result.stderr.length, |
| | 1573 | }), |
| | 1574 | ); |
| | 1575 | }), |
| | 1576 | ), |
| | 1577 | executeGit( |
| | 1578 | "GitVcsDriver.statusDetails.defaultRef", |
| | 1579 | cwd, |
| | 1580 | ["symbolic-ref", "refs/remotes/origin/HEAD"], |
| | 1581 | { allowNonZeroExit: true }, |
| | 1582 | ), |
| | 1583 | originRemoteExists(cwd).pipe(Effect.orElseSucceed(() => false)), |
| | 1584 | ], |
| | 1585 | { concurrency: "unbounded" }, |
| | 1586 | ); |
| 1535 | const statusStdout = statusResult.stdout; | 1587 | const statusStdout = statusResult.stdout; |
| 1536 | const defaultBranch = | 1588 | const defaultBranch = |
| 1537 | defaultRefResult.exitCode === 0 | 1589 | defaultRefResult.exitCode === 0 |
| ... | @@ -1592,14 +1644,10 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* | ... | @@ -1592,14 +1644,10 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* |
| 1592 | : yield* computeAheadCountAgainstBase(cwd, refName).pipe(Effect.orElseSucceed(() => 0)); | 1644 | : yield* computeAheadCountAgainstBase(cwd, refName).pipe(Effect.orElseSucceed(() => 0)); |
| 1593 | } | 1645 | } |
| 1594 | | 1646 | |
| 1595 | const stagedEntries = parseNumstatEntries(stagedNumstatStdout); | 1647 | const numstatEntries = parseNumstatEntries(numstatStdout); |
| 1596 | const unstagedEntries = parseNumstatEntries(unstagedNumstatStdout); | | |
| 1597 | const fileStatMap = new Map<string, { insertions: number; deletions: number }>(); | 1648 | const fileStatMap = new Map<string, { insertions: number; deletions: number }>(); |
| 1598 | for (const entry of [...stagedEntries, ...unstagedEntries]) { | 1649 | for (const entry of numstatEntries) { |
| 1599 | const existing = fileStatMap.get(entry.path) ?? { insertions: 0, deletions: 0 }; | 1650 | fileStatMap.set(entry.path, { insertions: entry.insertions, deletions: entry.deletions }); |
| 1600 | existing.insertions += entry.insertions; | | |
| 1601 | existing.deletions += entry.deletions; | | |
| 1602 | fileStatMap.set(entry.path, existing); | | |
| 1603 | } | 1651 | } |
| 1604 | | 1652 | |
| 1605 | let insertions = 0; | 1653 | let insertions = 0; |