@@ -8,10 +8,18 @@ import { Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider
88import { Repository , Resource } from './repository' ;
99import { IDisposable , filterEvent } from './util' ;
1010import { toGitUri } from './uri' ;
11- import { Branch , RefType , Status } from './api/git' ;
11+ import { Branch , RefType , UpstreamRef } from './api/git' ;
1212import { emojify , ensureEmojis } from './emoji' ;
1313import { Operation } from './operation' ;
1414
15+ function isBranchRefEqual ( brach1 : Branch | undefined , branch2 : Branch | undefined ) : boolean {
16+ return brach1 ?. name === branch2 ?. name && brach1 ?. commit === branch2 ?. commit ;
17+ }
18+
19+ function isUpstreamRefEqual ( upstream1 : UpstreamRef | undefined , upstream2 : UpstreamRef | undefined ) : boolean {
20+ return upstream1 ?. name === upstream2 ?. name && upstream1 ?. remote === upstream2 ?. remote && upstream1 ?. commit === upstream2 ?. commit ;
21+ }
22+
1523export class GitHistoryProvider implements SourceControlHistoryProvider , FileDecorationProvider , IDisposable {
1624
1725 private readonly _onDidChangeCurrentHistoryItemGroup = new EventEmitter < void > ( ) ;
@@ -21,10 +29,15 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
2129 readonly onDidChangeFileDecorations : Event < Uri [ ] > = this . _onDidChangeDecorations . event ;
2230
2331 private _HEAD : Branch | undefined ;
32+ private _HEADBase : UpstreamRef | undefined ;
2433 private _currentHistoryItemGroup : SourceControlHistoryItemGroup | undefined ;
2534
2635 get currentHistoryItemGroup ( ) : SourceControlHistoryItemGroup | undefined { return this . _currentHistoryItemGroup ; }
2736 set currentHistoryItemGroup ( value : SourceControlHistoryItemGroup | undefined ) {
37+ if ( this . _currentHistoryItemGroup === undefined && value === undefined ) {
38+ return ;
39+ }
40+
2841 this . _currentHistoryItemGroup = value ;
2942 this . _onDidChangeCurrentHistoryItemGroup . fire ( ) ;
3043 }
@@ -41,30 +54,31 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
4154 }
4255
4356 private async onDidRunGitStatus ( ) : Promise < void > {
44- // Check if HEAD has changed
45- if ( this . _HEAD ?. name === this . repository . HEAD ?. name &&
46- this . _HEAD ?. commit === this . repository . HEAD ?. commit &&
47- this . _HEAD ?. upstream ?. name === this . repository . HEAD ?. upstream ?. name &&
48- this . _HEAD ?. upstream ?. remote === this . repository . HEAD ?. upstream ?. remote &&
49- this . _HEAD ?. upstream ?. commit === this . repository . HEAD ?. upstream ?. commit ) {
57+ // Check if HEAD does not support incoming/outgoing (detached commit, tag)
58+ if ( ! this . repository . HEAD ?. name || ! this . repository . HEAD ?. commit || this . repository . HEAD . type === RefType . Tag ) {
59+ this . _HEAD = this . _HEADBase = undefined ;
60+ this . currentHistoryItemGroup = undefined ;
5061 return ;
5162 }
5263
53- this . _HEAD = this . repository . HEAD ;
64+ // Resolve HEAD base
65+ const HEADBase = await this . resolveHEADBase ( this . repository . HEAD ) ;
5466
55- // Check if HEAD supports incoming/outgoing (not a tag, not detached)
56- if ( ! this . _HEAD ?. name || ! this . _HEAD ?. commit || this . _HEAD . type === RefType . Tag ) {
57- this . currentHistoryItemGroup = undefined ;
67+ // Check if HEAD or HEADBase has changed
68+ if ( isBranchRefEqual ( this . _HEAD , this . repository . HEAD ) && isUpstreamRefEqual ( this . _HEADBase , HEADBase ) ) {
5869 return ;
5970 }
6071
72+ this . _HEAD = this . repository . HEAD ;
73+ this . _HEADBase = HEADBase ;
74+
6175 this . currentHistoryItemGroup = {
62- id : `refs/heads/${ this . _HEAD . name } ` ,
63- label : this . _HEAD . name ,
64- upstream : this . _HEAD . upstream ?
76+ id : `refs/heads/${ this . _HEAD . name ?? '' } ` ,
77+ label : this . _HEAD . name ?? '' ,
78+ base : this . _HEADBase ?
6579 {
66- id : `refs/remotes/${ this . _HEAD . upstream . remote } /${ this . _HEAD . upstream . name } ` ,
67- label : `${ this . _HEAD . upstream . remote } /${ this . _HEAD . upstream . name } ` ,
80+ id : `refs/remotes/${ this . _HEADBase . remote } /${ this . _HEADBase . name } ` ,
81+ label : `${ this . _HEADBase . remote } /${ this . _HEADBase . name } ` ,
6882 } : undefined
6983 } ;
7084 }
@@ -138,7 +152,10 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
138152 } ) ;
139153
140154 // History item change decoration
141- const fileDecoration = this . getHistoryItemChangeFileDecoration ( change . status ) ;
155+ const letter = Resource . getStatusLetter ( change . status ) ;
156+ const tooltip = Resource . getStatusText ( change . status ) ;
157+ const color = Resource . getStatusColor ( change . status ) ;
158+ const fileDecoration = new FileDecoration ( letter , tooltip , color ) ;
142159 this . historyItemDecorations . set ( historyItemUri . toString ( ) , fileDecoration ) ;
143160
144161 historyItemChangesUri . push ( historyItemUri ) ;
@@ -148,40 +165,6 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
148165 return historyItemChanges ;
149166 }
150167
151- async resolveHistoryItemGroupBase ( historyItemGroupId : string ) : Promise < SourceControlHistoryItemGroup | undefined > {
152- // TODO - support for all history item groups
153- if ( historyItemGroupId !== this . currentHistoryItemGroup ?. id ) {
154- return undefined ;
155- }
156-
157- if ( this . currentHistoryItemGroup ?. upstream ) {
158- return this . currentHistoryItemGroup . upstream ;
159- }
160-
161- // Branch base
162- try {
163- const branchBase = await this . repository . getBranchBase ( historyItemGroupId ) ;
164-
165- if ( branchBase ?. name && branchBase ?. type === RefType . Head ) {
166- return {
167- id : `refs/heads/${ branchBase . name } ` ,
168- label : branchBase . name
169- } ;
170- }
171- if ( branchBase ?. name && branchBase . remote && branchBase ?. type === RefType . RemoteHead ) {
172- return {
173- id : `refs/remotes/${ branchBase . remote } /${ branchBase . name } ` ,
174- label : `${ branchBase . remote } /${ branchBase . name } `
175- } ;
176- }
177- }
178- catch ( err ) {
179- this . logger . error ( `Failed to get branch base for '${ historyItemGroupId } ': ${ err . message } ` ) ;
180- }
181-
182- return undefined ;
183- }
184-
185168 async resolveHistoryItemGroupCommonAncestor ( refId1 : string , refId2 : string ) : Promise < { id : string ; ahead : number ; behind : number } | undefined > {
186169 const ancestor = await this . repository . getMergeBase ( refId1 , refId2 ) ;
187170 if ( ! ancestor ) {
@@ -202,12 +185,29 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec
202185 return this . historyItemDecorations . get ( uri . toString ( ) ) ;
203186 }
204187
205- private getHistoryItemChangeFileDecoration ( status : Status ) : FileDecoration {
206- const letter = Resource . getStatusLetter ( status ) ;
207- const tooltip = Resource . getStatusText ( status ) ;
208- const color = Resource . getStatusColor ( status ) ;
188+ private async resolveHEADBase ( HEAD : Branch ) : Promise < UpstreamRef | undefined > {
189+ // Upstream
190+ if ( HEAD . upstream ) {
191+ return HEAD . upstream ;
192+ }
209193
210- return new FileDecoration ( letter , tooltip , color ) ;
194+ try {
195+ const remoteBranch = await this . repository . getBranchBase ( HEAD . name ?? '' ) ;
196+ if ( ! remoteBranch ?. remote || ! remoteBranch ?. name || ! remoteBranch ?. commit || remoteBranch ?. type !== RefType . RemoteHead ) {
197+ return undefined ;
198+ }
199+
200+ return {
201+ name : remoteBranch . name ,
202+ remote : remoteBranch . remote ,
203+ commit : remoteBranch . commit
204+ } ;
205+ }
206+ catch ( err ) {
207+ this . logger . error ( `Failed to get branch base for '${ HEAD . name } ': ${ err . message } ` ) ;
208+ }
209+
210+ return undefined ;
211211 }
212212
213213 dispose ( ) : void {
0 commit comments