66
77import * as pathLib from 'path' ;
88import * as vscode from 'vscode' ;
9- import { Repository } from './api/api' ;
109import { GitErrorCodes } from './api/api1' ;
1110import { CommentReply , findActiveHandler , resolveCommentHandler } from './commentHandlerResolver' ;
1211import { COPILOT_LOGINS } from './common/copilot' ;
@@ -56,7 +55,7 @@ const DISCARD_CHANGES = vscode.l10n.t('Discard changes');
5655 * @param repository The git repository with uncommitted changes
5756 * @returns Promise<boolean> true if user chose to proceed (after staging/discarding), false if cancelled
5857 */
59- async function handleUncommittedChanges ( repository : Repository ) : Promise < boolean > {
58+ async function handleUncommittedChanges ( repository : any ) : Promise < boolean > {
6059 const hasWorkingTreeChanges = repository . state . workingTreeChanges . length > 0 ;
6160 const hasIndexChanges = repository . state . indexChanges . length > 0 ;
6261
@@ -521,19 +520,19 @@ export function registerCommands(
521520 ) ;
522521 }
523522
524- function isSourceControl ( x : any ) : x is Repository {
523+ function isSourceControl ( x : any ) : x is any {
525524 return ! ! x ?. rootUri ;
526525 }
527526
528527 context . subscriptions . push (
529528 vscode . commands . registerCommand (
530529 'pr.create' ,
531- async ( args ?: { repoPath : string ; compareBranch : string } | Repository ) => {
530+ async ( args ?: { repoPath : string ; compareBranch : string } | any ) => {
532531 // The arguments this is called with are either from the SCM view, or manually passed.
533532 if ( isSourceControl ( args ) ) {
534533 ( await chooseReviewManager ( args . rootUri . fsPath ) ) ?. createPullRequest ( ) ;
535534 } else {
536- ( await chooseReviewManager ( args ?. repoPath ) ) ?. createPullRequest ( args ?. compareBranch ) ;
535+ ( await chooseReviewManager ( ( args as any ) ?. repoPath ) ) ?. createPullRequest ( ( args as any ) ?. compareBranch ) ;
537536 }
538537 } ,
539538 ) ,
@@ -542,7 +541,7 @@ export function registerCommands(
542541 context . subscriptions . push (
543542 vscode . commands . registerCommand (
544543 'pr.pushAndCreate' ,
545- async ( args ?: any | Repository ) => {
544+ async ( args ?: any ) => {
546545 if ( isSourceControl ( args ) ) {
547546 const reviewManager = await chooseReviewManager ( args . rootUri . fsPath ) ;
548547 const folderManager = reposManager . getManagerForFile ( args . rootUri ) ;
@@ -577,7 +576,7 @@ export function registerCommands(
577576 }
578577
579578 let pullRequestModel : PullRequestModel ;
580- let repository : Repository | undefined ;
579+ let repository : any | undefined ;
581580
582581 if ( pr instanceof PRNode || pr instanceof RepositoryChangesNode ) {
583582 pullRequestModel = pr . pullRequestModel ;
@@ -654,6 +653,79 @@ export function registerCommands(
654653 } ) ,
655654 ) ;
656655
656+ context . subscriptions . push (
657+ vscode . commands . registerCommand ( 'pr.openCommitChanges' , async ( commitShaOrUrl : string , folderManager ?: FolderRepositoryManager ) => {
658+ try {
659+ // Extract commit SHA from GitHub URL if needed
660+ let commitSha : string ;
661+ if ( commitShaOrUrl . includes ( 'github.com' ) && commitShaOrUrl . includes ( '/commit/' ) ) {
662+ const match = commitShaOrUrl . match ( / \/ c o m m i t \/ ( [ a - f 0 - 9 ] { 7 , 40 } ) / ) ;
663+ if ( ! match ) {
664+ vscode . window . showErrorMessage ( vscode . l10n . t ( 'Invalid commit URL: {0}' , commitShaOrUrl ) ) ;
665+ return ;
666+ }
667+ commitSha = match [ 1 ] ;
668+ } else {
669+ commitSha = commitShaOrUrl ;
670+ }
671+
672+ // Use provided folder manager or try to find one for the active workspace
673+ if ( ! folderManager ) {
674+ folderManager = reposManager . folderManagers . find ( fm => fm . repository . rootUri . fsPath === vscode . workspace . workspaceFolders ?. [ 0 ] ?. uri . fsPath ) ;
675+ if ( ! folderManager ) {
676+ vscode . window . showErrorMessage ( vscode . l10n . t ( 'No repository found to show commit changes.' ) ) ;
677+ return ;
678+ }
679+ }
680+
681+ const repository = folderManager . repository ;
682+
683+ // Get the changes for this commit
684+ const changes = await repository . diffBetween ( commitSha + '^' , commitSha ) ;
685+
686+ if ( changes . length === 0 ) {
687+ vscode . window . showInformationMessage ( vscode . l10n . t ( 'No file changes found in commit {0}' , commitSha . substring ( 0 , 7 ) ) ) ;
688+ return ;
689+ }
690+
691+ // Build arguments for vscode.changes command
692+ const args : [ vscode . Uri , vscode . Uri | undefined , vscode . Uri | undefined ] [ ] = [ ] ;
693+ for ( const change of changes ) {
694+ // For each changed file, create URIs for before and after
695+ const parentCommit = commitSha + '^' ;
696+ let beforeUri : vscode . Uri | undefined ;
697+ let afterUri : vscode . Uri | undefined ;
698+
699+ if ( change . status === 7 ) { // DELETED
700+ // File was deleted, show old version vs empty
701+ beforeUri = change . uri . with ( { scheme : 'git' , authority : parentCommit } ) ;
702+ afterUri = undefined ;
703+ } else if ( change . status === 1 || change . status === 11 ) { // INDEX_ADDED || ADDED_BY_US
704+ // File was added, show empty vs new version
705+ beforeUri = undefined ;
706+ afterUri = change . uri . with ( { scheme : 'git' , authority : commitSha } ) ;
707+ } else {
708+ // File was modified, show old vs new
709+ beforeUri = change . uri . with ( { scheme : 'git' , authority : parentCommit } ) ;
710+ afterUri = change . uri . with ( { scheme : 'git' , authority : commitSha } ) ;
711+ }
712+
713+ args . push ( [ change . uri , beforeUri , afterUri ] ) ;
714+ }
715+
716+ // Send telemetry
717+ folderManager . telemetry . sendTelemetryEvent ( 'pr.openCommitChanges' ) ;
718+
719+ // Open multi diff editor
720+ return vscode . commands . executeCommand ( 'vscode.changes' , vscode . l10n . t ( 'Changes in Commit {0}' , commitSha . substring ( 0 , 7 ) ) , args ) ;
721+
722+ } catch ( error ) {
723+ Logger . error ( `Failed to open commit changes: ${ formatError ( error ) } ` , 'Commands' ) ;
724+ vscode . window . showErrorMessage ( vscode . l10n . t ( 'Failed to open commit changes: {0}' , formatError ( error ) ) ) ;
725+ }
726+ } ) ,
727+ ) ;
728+
657729 let isCheckingOutFromReadonlyFile = false ;
658730 context . subscriptions . push ( vscode . commands . registerCommand ( 'pr.checkoutFromReadonlyFile' , async ( ) => {
659731 const uri = vscode . window . activeTextEditor ?. document . uri ;
0 commit comments