11import { JupyterFrontEnd } from '@jupyterlab/application' ;
22import { IChangedArgs , PathExt } from '@jupyterlab/coreutils' ;
3+ import { IDocumentManager } from '@jupyterlab/docmanager' ;
34import { ServerConnection } from '@jupyterlab/services' ;
45import { ISettingRegistry } from '@jupyterlab/settingregistry' ;
56import { LinkedList } from '@lumino/collections' ;
@@ -28,11 +29,13 @@ export class GitExtension implements IGitExtension {
2829 constructor (
2930 serverRoot : string ,
3031 app : JupyterFrontEnd = null ,
32+ docmanager : IDocumentManager = null ,
3133 settings ?: ISettingRegistry . ISettings
3234 ) {
3335 const self = this ;
3436 this . _serverRoot = serverRoot ;
3537 this . _app = app ;
38+ this . _docmanager = docmanager ;
3639 this . _settings = settings || null ;
3740
3841 let interval : number ;
@@ -459,6 +462,20 @@ export class GitExtension implements IGitExtension {
459462 const tid = this . _addTask ( 'git:checkout' ) ;
460463 try {
461464 response = await httpGitRequest ( '/git/checkout' , 'POST' , body ) ;
465+
466+ if ( response . ok ) {
467+ if ( body . checkout_branch ) {
468+ const files = ( await this . changedFiles (
469+ this . _currentBranch . name ,
470+ body . branchname
471+ ) ) [ 'files' ] ;
472+ if ( files ) {
473+ files . forEach ( file => this . _revertFile ( file ) ) ;
474+ }
475+ } else {
476+ this . _revertFile ( options . filename ) ;
477+ }
478+ }
462479 } catch ( err ) {
463480 throw new ServerConnection . NetworkError ( err ) ;
464481 } finally {
@@ -468,6 +485,7 @@ export class GitExtension implements IGitExtension {
468485 if ( ! response . ok ) {
469486 throw new ServerConnection . ResponseError ( response , data . message ) ;
470487 }
488+
471489 if ( body . checkout_branch ) {
472490 await this . refreshBranch ( ) ;
473491 this . _headChanged . emit ( ) ;
@@ -609,11 +627,17 @@ export class GitExtension implements IGitExtension {
609627 return Promise . resolve ( new Response ( JSON . stringify ( response ) ) ) ;
610628 }
611629 const tid = this . _addTask ( 'git:commit:revert' ) ;
630+ const files = ( await this . changedFiles ( null , null , hash + '^!' ) ) [ 'files' ] ;
612631 try {
613632 response = await httpGitRequest ( '/git/delete_commit' , 'POST' , {
614633 commit_id : hash ,
615634 top_repo_path : path
616635 } ) ;
636+ if ( response . ok && files ) {
637+ files . forEach ( file => {
638+ this . _revertFile ( file ) ;
639+ } ) ;
640+ }
617641 } catch ( err ) {
618642 throw new ServerConnection . NetworkError ( err ) ;
619643 } finally {
@@ -905,12 +929,29 @@ export class GitExtension implements IGitExtension {
905929 return Promise . resolve ( new Response ( JSON . stringify ( response ) ) ) ;
906930 }
907931 const tid = this . _addTask ( 'git:reset:changes' ) ;
932+ const reset_all = filename === undefined ;
933+ let files ;
934+ if ( reset_all ) {
935+ files = ( await this . changedFiles ( 'INDEX' , 'HEAD' ) ) [ 'files' ] ;
936+ }
908937 try {
909938 response = await httpGitRequest ( '/git/reset' , 'POST' , {
910939 reset_all : filename === undefined ,
911940 filename : filename === undefined ? null : filename ,
912941 top_repo_path : path
913942 } ) ;
943+
944+ if ( response . ok ) {
945+ if ( reset_all ) {
946+ if ( files ) {
947+ files . forEach ( file => {
948+ this . _revertFile ( file ) ;
949+ } ) ;
950+ }
951+ } else {
952+ this . _revertFile ( filename ) ;
953+ }
954+ }
914955 } catch ( err ) {
915956 throw new ServerConnection . NetworkError ( err ) ;
916957 } finally {
@@ -947,12 +988,20 @@ export class GitExtension implements IGitExtension {
947988 } ;
948989 return Promise . resolve ( new Response ( JSON . stringify ( response ) ) ) ;
949990 }
991+ const files = ( await this . changedFiles ( null , null , hash ) ) [ 'files' ] ;
950992 const tid = this . _addTask ( 'git:reset:hard' ) ;
951993 try {
952994 response = await httpGitRequest ( '/git/reset_to_commit' , 'POST' , {
953995 commit_id : hash ,
954996 top_repo_path : path
955997 } ) ;
998+ if ( response . ok ) {
999+ if ( files ) {
1000+ files . forEach ( file => {
1001+ this . _revertFile ( file ) ;
1002+ } ) ;
1003+ }
1004+ }
9561005 } catch ( err ) {
9571006 throw new ServerConnection . NetworkError ( err ) ;
9581007 } finally {
@@ -1226,6 +1275,37 @@ export class GitExtension implements IGitExtension {
12261275 return Promise . resolve ( response ) ;
12271276 }
12281277
1278+ /**
1279+ * Get list of files changed between two commits or two branches
1280+ * @param base id of base commit or base branch for comparison
1281+ * @param remote id of remote commit or remote branch for comparison
1282+ * @param singleCommit id of a single commit
1283+ *
1284+ * @returns the names of the changed files
1285+ */
1286+ async changedFiles (
1287+ base ?: string ,
1288+ remote ?: string ,
1289+ singleCommit ?: string
1290+ ) : Promise < Git . IChangedFilesResult > {
1291+ try {
1292+ const response = await httpGitRequest ( '/git/changed_files' , 'POST' , {
1293+ current_path : this . pathRepository ,
1294+ base : base ,
1295+ remote : remote ,
1296+ single_commit : singleCommit
1297+ } ) ;
1298+ if ( ! response . ok ) {
1299+ return response . json ( ) . then ( ( data : any ) => {
1300+ throw new ServerConnection . ResponseError ( response , data . message ) ;
1301+ } ) ;
1302+ }
1303+ return response . json ( ) ;
1304+ } catch ( err ) {
1305+ throw new ServerConnection . NetworkError ( err ) ;
1306+ }
1307+ }
1308+
12291309 /**
12301310 * Make request for a list of all git branches in the repository
12311311 * Retrieve a list of repository branches.
@@ -1344,12 +1424,26 @@ export class GitExtension implements IGitExtension {
13441424 return this . _taskID ;
13451425 }
13461426
1427+ /**
1428+ * if file is open in JupyterLab find the widget and ensure the JupyterLab
1429+ * version matches the version on disk. Do nothing if the file has unsaved changes
1430+ *
1431+ * @param path path to the file to be reverted
1432+ */
1433+ private _revertFile ( path : string ) : void {
1434+ const widget = this . _docmanager . findWidget ( this . getRelativeFilePath ( path ) ) ;
1435+ if ( widget && ! widget . context . model . dirty ) {
1436+ widget . context . revert ( ) ;
1437+ }
1438+ }
1439+
13471440 private _status : Git . IStatusFile [ ] = [ ] ;
13481441 private _pathRepository : string | null = null ;
13491442 private _branches : Git . IBranch [ ] ;
13501443 private _currentBranch : Git . IBranch ;
13511444 private _serverRoot : string ;
13521445 private _app : JupyterFrontEnd | null ;
1446+ private _docmanager : IDocumentManager | null ;
13531447 private _diffProviders : { [ key : string ] : Git . IDiffCallback } = { } ;
13541448 private _isDisposed = false ;
13551449 private _markerCache : Markers = new Markers ( ( ) => this . _markChanged . emit ( ) ) ;
0 commit comments