55
66import { commands , Uri , Command , EventEmitter , Event , scm , SourceControl , SourceControlInputBox , SourceControlResourceGroup , SourceControlResourceState , SourceControlResourceDecorations , SourceControlInputBoxValidation , Disposable , ProgressLocation , window , workspace , WorkspaceEdit , ThemeColor , DecorationData , Memento , SourceControlInputBoxValidationType , OutputChannel , LogLevel , env } from 'vscode' ;
77import { Repository as BaseRepository , Commit , Stash , GitError , Submodule , CommitOptions , ForcePushMode } from './git' ;
8- import { anyEvent , filterEvent , eventToPromise , dispose , find , isDescendant , IDisposable , onceEvent , EmptyDisposable , debounceEvent , toDisposable , combinedDisposable } from './util' ;
8+ import { anyEvent , filterEvent , eventToPromise , dispose , find , isDescendant , IDisposable , onceEvent , EmptyDisposable , debounceEvent , combinedDisposable , watch , IFileWatcher } from './util' ;
99import { memoize , throttle , debounce } from './decorators' ;
1010import { toGitUri } from './uri' ;
1111import { AutoFetcher } from './autofetch' ;
@@ -480,6 +480,49 @@ class FileEventLogger {
480480 }
481481}
482482
483+ class DotGitWatcher implements IFileWatcher {
484+
485+ readonly event : Event < Uri > ;
486+
487+ private emitter = new EventEmitter < Uri > ( ) ;
488+ private transientDisposables : IDisposable [ ] = [ ] ;
489+ private disposables : IDisposable [ ] = [ ] ;
490+
491+ constructor ( private repository : Repository ) {
492+ const rootWatcher = watch ( repository . dotGit ) ;
493+ this . disposables . push ( rootWatcher ) ;
494+
495+ const filteredRootWatcher = filterEvent ( rootWatcher . event , uri => ! / \/ \. g i t ( \/ i n d e x \. l o c k ) ? $ / . test ( uri . path ) ) ;
496+ this . event = anyEvent ( filteredRootWatcher , this . emitter . event ) ;
497+
498+ repository . onDidRunGitStatus ( this . updateTransientWatchers , this , this . disposables ) ;
499+ this . updateTransientWatchers ( ) ;
500+ }
501+
502+ private updateTransientWatchers ( ) {
503+ this . transientDisposables = dispose ( this . transientDisposables ) ;
504+
505+ if ( ! this . repository . HEAD || ! this . repository . HEAD . upstream ) {
506+ return ;
507+ }
508+
509+ this . transientDisposables = dispose ( this . transientDisposables ) ;
510+
511+ const { name, remote } = this . repository . HEAD . upstream ;
512+ const upstreamPath = path . join ( this . repository . dotGit , 'refs' , 'remotes' , remote , name ) ;
513+
514+ const upstreamWatcher = watch ( upstreamPath ) ;
515+ this . transientDisposables . push ( upstreamWatcher ) ;
516+ upstreamWatcher . event ( this . emitter . fire , this . emitter , this . transientDisposables ) ;
517+ }
518+
519+ dispose ( ) {
520+ this . emitter . dispose ( ) ;
521+ this . transientDisposables = dispose ( this . transientDisposables ) ;
522+ this . disposables = dispose ( this . disposables ) ;
523+ }
524+ }
525+
483526export class Repository implements Disposable {
484527
485528 private _onDidChangeRepository = new EventEmitter < Uri > ( ) ;
@@ -577,11 +620,14 @@ export class Repository implements Disposable {
577620 return this . repository . root ;
578621 }
579622
623+ get dotGit ( ) : string {
624+ return this . repository . dotGit ;
625+ }
626+
580627 private isRepositoryHuge = false ;
581628 private didWarnAboutLimit = false ;
582629 private isFreshRepository : boolean | undefined = undefined ;
583630
584-
585631 private disposables : Disposable [ ] = [ ] ;
586632
587633 constructor (
@@ -596,23 +642,19 @@ export class Repository implements Disposable {
596642 const onWorkspaceRepositoryFileChange = filterEvent ( onWorkspaceFileChange , uri => isDescendant ( repository . root , uri . fsPath ) ) ;
597643 const onWorkspaceWorkingTreeFileChange = filterEvent ( onWorkspaceRepositoryFileChange , uri => ! / \/ \. g i t ( $ | \/ ) / . test ( uri . path ) ) ;
598644
599- const dotGitWatcher = fs . watch ( repository . dotGit ) ;
600- const onDotGitFileChangeEmitter = new EventEmitter < Uri > ( ) ;
601- dotGitWatcher . on ( 'change' , ( _ , e ) => onDotGitFileChangeEmitter . fire ( Uri . file ( path . join ( repository . dotGit , e as string ) ) ) ) ;
602- dotGitWatcher . on ( 'error' , err => console . error ( err ) ) ;
603- this . disposables . push ( toDisposable ( ( ) => dotGitWatcher . close ( ) ) ) ;
604- const onDotGitFileChange = filterEvent ( onDotGitFileChangeEmitter . event , uri => ! / \/ \. g i t ( \/ i n d e x \. l o c k ) ? $ / . test ( uri . path ) ) ;
645+ const dotGitFileWatcher = new DotGitWatcher ( this ) ;
646+ this . disposables . push ( dotGitFileWatcher ) ;
605647
606648 // FS changes should trigger `git status`:
607649 // - any change inside the repository working tree
608650 // - any change whithin the first level of the `.git` folder, except the folder itself and `index.lock`
609- const onFileChange = anyEvent ( onWorkspaceWorkingTreeFileChange , onDotGitFileChange ) ;
651+ const onFileChange = anyEvent ( onWorkspaceWorkingTreeFileChange , dotGitFileWatcher . event ) ;
610652 onFileChange ( this . onFileChange , this , this . disposables ) ;
611653
612654 // Relevate repository changes should trigger virtual document change events
613- onDotGitFileChange ( this . _onDidChangeRepository . fire , this . _onDidChangeRepository , this . disposables ) ;
655+ dotGitFileWatcher . event ( this . _onDidChangeRepository . fire , this . _onDidChangeRepository , this . disposables ) ;
614656
615- this . disposables . push ( new FileEventLogger ( onWorkspaceWorkingTreeFileChange , onDotGitFileChange , outputChannel ) ) ;
657+ this . disposables . push ( new FileEventLogger ( onWorkspaceWorkingTreeFileChange , dotGitFileWatcher . event , outputChannel ) ) ;
616658
617659 const root = Uri . file ( repository . root ) ;
618660 this . _sourceControl = scm . createSourceControl ( 'git' , 'Git' , root ) ;
0 commit comments