@@ -8,9 +8,13 @@ import {
88 GitCommandError ,
99 GitRunStackedActionResult ,
1010 GitStackedAction ,
11+ type GitStatusLocalResult ,
12+ type GitStatusRemoteResult ,
1113 ModelSelection ,
1214} from "@t3tools/contracts" ;
1315import {
16+ detectGitHostingProviderFromRemoteUrl ,
17+ mergeGitStatusParts ,
1418 resolveAutoFeatureBranchName ,
1519 sanitizeBranchFragment ,
1620 sanitizeFeatureBranchName ,
@@ -695,26 +699,55 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
695699
696700 const tempDir = process . env . TMPDIR ?? process . env . TEMP ?? process . env . TMP ?? "/tmp" ;
697701 const normalizeStatusCacheKey = ( cwd : string ) => canonicalizeExistingPath ( cwd ) ;
698- const readStatus = Effect . fn ( "readStatus" ) ( function * ( cwd : string ) {
699- const details = yield * gitCore . statusDetails ( cwd ) . pipe (
700- Effect . catchIf ( isNotGitRepositoryError , ( ) =>
701- Effect . succeed ( {
702- isRepo : false ,
703- hasOriginRemote : false ,
704- isDefaultBranch : false ,
705- branch : null ,
706- upstreamRef : null ,
707- hasWorkingTreeChanges : false ,
708- workingTree : { files : [ ] , insertions : 0 , deletions : 0 } ,
709- hasUpstream : false ,
710- aheadCount : 0 ,
711- behindCount : 0 ,
712- } satisfies GitStatusDetails ) ,
713- ) ,
714- ) ;
702+ const nonRepositoryStatusDetails = {
703+ isRepo : false ,
704+ hasOriginRemote : false ,
705+ isDefaultBranch : false ,
706+ branch : null ,
707+ upstreamRef : null ,
708+ hasWorkingTreeChanges : false ,
709+ workingTree : { files : [ ] , insertions : 0 , deletions : 0 } ,
710+ hasUpstream : false ,
711+ aheadCount : 0 ,
712+ behindCount : 0 ,
713+ } satisfies GitStatusDetails ;
714+ const readLocalStatus = Effect . fn ( "readLocalStatus" ) ( function * ( cwd : string ) {
715+ const details = yield * gitCore
716+ . statusDetailsLocal ( cwd )
717+ . pipe (
718+ Effect . catchIf ( isNotGitRepositoryError , ( ) => Effect . succeed ( nonRepositoryStatusDetails ) ) ,
719+ ) ;
720+ const hostingProvider = details . isRepo
721+ ? yield * resolveHostingProvider ( cwd , details . branch )
722+ : null ;
723+
724+ return {
725+ isRepo : details . isRepo ,
726+ ...( hostingProvider ? { hostingProvider } : { } ) ,
727+ hasOriginRemote : details . hasOriginRemote ,
728+ isDefaultBranch : details . isDefaultBranch ,
729+ branch : details . branch ,
730+ hasWorkingTreeChanges : details . hasWorkingTreeChanges ,
731+ workingTree : details . workingTree ,
732+ } satisfies GitStatusLocalResult ;
733+ } ) ;
734+ const localStatusResultCache = yield * Cache . makeWith ( {
735+ capacity : STATUS_RESULT_CACHE_CAPACITY ,
736+ lookup : readLocalStatus ,
737+ timeToLive : ( exit ) => ( Exit . isSuccess ( exit ) ? STATUS_RESULT_CACHE_TTL : Duration . zero ) ,
738+ } ) ;
739+ const invalidateLocalStatusResultCache = ( cwd : string ) =>
740+ Cache . invalidate ( localStatusResultCache , normalizeStatusCacheKey ( cwd ) ) ;
741+ const readRemoteStatus = Effect . fn ( "readRemoteStatus" ) ( function * ( cwd : string ) {
742+ const details = yield * gitCore
743+ . statusDetails ( cwd )
744+ . pipe ( Effect . catchIf ( isNotGitRepositoryError , ( ) => Effect . succeed ( null ) ) ) ;
745+ if ( details === null || ! details . isRepo ) {
746+ return null ;
747+ }
715748
716749 const pr =
717- details . isRepo && details . branch !== null
750+ details . branch !== null
718751 ? yield * findLatestPr ( cwd , {
719752 branch : details . branch ,
720753 upstreamRef : details . upstreamRef ,
@@ -725,29 +758,38 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
725758 : null ;
726759
727760 return {
728- isRepo : details . isRepo ,
729- hasOriginRemote : details . hasOriginRemote ,
730- isDefaultBranch : details . isDefaultBranch ,
731- branch : details . branch ,
732- hasWorkingTreeChanges : details . hasWorkingTreeChanges ,
733- workingTree : details . workingTree ,
734761 hasUpstream : details . hasUpstream ,
735762 aheadCount : details . aheadCount ,
736763 behindCount : details . behindCount ,
737764 pr,
738- } ;
765+ } satisfies GitStatusRemoteResult ;
739766 } ) ;
740- const statusResultCache = yield * Cache . makeWith ( {
767+ const remoteStatusResultCache = yield * Cache . makeWith ( {
741768 capacity : STATUS_RESULT_CACHE_CAPACITY ,
742- lookup : readStatus ,
769+ lookup : readRemoteStatus ,
743770 timeToLive : ( exit ) => ( Exit . isSuccess ( exit ) ? STATUS_RESULT_CACHE_TTL : Duration . zero ) ,
744771 } ) ;
745- const invalidateStatusResultCache = ( cwd : string ) =>
746- Cache . invalidate ( statusResultCache , normalizeStatusCacheKey ( cwd ) ) ;
772+ const invalidateRemoteStatusResultCache = ( cwd : string ) =>
773+ Cache . invalidate ( remoteStatusResultCache , normalizeStatusCacheKey ( cwd ) ) ;
747774
748775 const readConfigValueNullable = ( cwd : string , key : string ) =>
749776 gitCore . readConfigValue ( cwd , key ) . pipe ( Effect . catch ( ( ) => Effect . succeed ( null ) ) ) ;
750777
778+ const resolveHostingProvider = Effect . fn ( "resolveHostingProvider" ) ( function * (
779+ cwd : string ,
780+ branch : string | null ,
781+ ) {
782+ const preferredRemoteName =
783+ branch === null
784+ ? "origin"
785+ : ( ( yield * readConfigValueNullable ( cwd , `branch.${ branch } .remote` ) ) ?? "origin" ) ;
786+ const remoteUrl =
787+ ( yield * readConfigValueNullable ( cwd , `remote.${ preferredRemoteName } .url` ) ) ??
788+ ( yield * readConfigValueNullable ( cwd , "remote.origin.url" ) ) ;
789+
790+ return remoteUrl ? detectGitHostingProviderFromRemoteUrl ( remoteUrl ) : null ;
791+ } ) ;
792+
751793 const resolveRemoteRepositoryContext = Effect . fn ( "resolveRemoteRepositoryContext" ) ( function * (
752794 cwd : string ,
753795 remoteName : string | null ,
@@ -1311,9 +1353,34 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
13111353 } ;
13121354 } ) ;
13131355
1356+ const localStatus : GitManagerShape [ "localStatus" ] = Effect . fn ( "localStatus" ) ( function * ( input ) {
1357+ return yield * Cache . get ( localStatusResultCache , normalizeStatusCacheKey ( input . cwd ) ) ;
1358+ } ) ;
1359+ const remoteStatus : GitManagerShape [ "remoteStatus" ] = Effect . fn ( "remoteStatus" ) (
1360+ function * ( input ) {
1361+ return yield * Cache . get ( remoteStatusResultCache , normalizeStatusCacheKey ( input . cwd ) ) ;
1362+ } ,
1363+ ) ;
13141364 const status : GitManagerShape [ "status" ] = Effect . fn ( "status" ) ( function * ( input ) {
1315- return yield * Cache . get ( statusResultCache , normalizeStatusCacheKey ( input . cwd ) ) ;
1365+ const [ local , remote ] = yield * Effect . all ( [ localStatus ( input ) , remoteStatus ( input ) ] ) ;
1366+ return mergeGitStatusParts ( local , remote ) ;
13161367 } ) ;
1368+ const invalidateLocalStatus : GitManagerShape [ "invalidateLocalStatus" ] = Effect . fn (
1369+ "invalidateLocalStatus" ,
1370+ ) ( function * ( cwd ) {
1371+ yield * invalidateLocalStatusResultCache ( cwd ) ;
1372+ } ) ;
1373+ const invalidateRemoteStatus : GitManagerShape [ "invalidateRemoteStatus" ] = Effect . fn (
1374+ "invalidateRemoteStatus" ,
1375+ ) ( function * ( cwd ) {
1376+ yield * invalidateRemoteStatusResultCache ( cwd ) ;
1377+ } ) ;
1378+ const invalidateStatus : GitManagerShape [ "invalidateStatus" ] = Effect . fn ( "invalidateStatus" ) (
1379+ function * ( cwd ) {
1380+ yield * invalidateLocalStatusResultCache ( cwd ) ;
1381+ yield * invalidateRemoteStatusResultCache ( cwd ) ;
1382+ } ,
1383+ ) ;
13171384
13181385 const resolvePullRequest : GitManagerShape [ "resolvePullRequest" ] = Effect . fn ( "resolvePullRequest" ) (
13191386 function * ( input ) {
@@ -1488,7 +1555,7 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
14881555 branch : worktree . worktree . branch ,
14891556 worktreePath : worktree . worktree . path ,
14901557 } ;
1491- } ) . pipe ( Effect . ensuring ( invalidateStatusResultCache ( input . cwd ) ) ) ;
1558+ } ) . pipe ( Effect . ensuring ( invalidateStatus ( input . cwd ) ) ) ;
14921559 } ) ;
14931560
14941561 const runFeatureBranchStep = Effect . fn ( "runFeatureBranchStep" ) ( function * (
@@ -1692,7 +1759,7 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
16921759 } ) ;
16931760
16941761 return yield * runAction ( ) . pipe (
1695- Effect . ensuring ( invalidateStatusResultCache ( input . cwd ) ) ,
1762+ Effect . ensuring ( invalidateStatus ( input . cwd ) ) ,
16961763 Effect . tapError ( ( error ) =>
16971764 Effect . flatMap ( Ref . get ( currentPhase ) , ( phase ) =>
16981765 progress . emit ( {
@@ -1707,7 +1774,12 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
17071774 ) ;
17081775
17091776 return {
1777+ localStatus,
1778+ remoteStatus,
17101779 status,
1780+ invalidateLocalStatus,
1781+ invalidateRemoteStatus,
1782+ invalidateStatus,
17111783 resolvePullRequest,
17121784 preparePullRequestThread,
17131785 runStackedAction,
0 commit comments