Skip to content

Commit

Permalink
#516 New "Reset File to this Revision..." action on the File Context …
Browse files Browse the repository at this point in the history
…Menu in the Commit Details View.
  • Loading branch information
mhutchie committed May 30, 2021
1 parent b4f9f77 commit a50bafd
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 3 deletions.
14 changes: 14 additions & 0 deletions src/dataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,20 @@ export class DataSource extends Disposable {
}


/* Git Action Methods - File */

/**
* Reset a file to the specified revision.
* @param repo The path of the repository.
* @param commitHash The commit to reset the file to.
* @param filePath The file to reset.
* @returns The ErrorInfo from the executed command.
*/
public resetFileToRevision(repo: string, commitHash: string, filePath: string) {
return this.runGitCommand(['checkout', commitHash, '--', filePath], repo);
}


/* Git Action Methods - Stash */

/**
Expand Down
6 changes: 6 additions & 0 deletions src/gitGraphView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,12 @@ export class GitGraphView extends Disposable {
showErrorMessage('No Git repositories were found in the current workspace.');
}
break;
case 'resetFileToRevision':
this.sendMessage({
command: 'resetFileToRevision',
error: await this.dataSource.resetFileToRevision(msg.repo, msg.commitHash, msg.filePath)
});
break;
case 'resetToCommit':
this.sendMessage({
command: 'resetToCommit',
Expand Down
11 changes: 11 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1108,6 +1108,15 @@ export interface RequestRescanForRepos extends BaseMessage {
readonly command: 'rescanForRepos';
}

export interface RequestResetFileToRevision extends RepoRequest {
readonly command: 'resetFileToRevision';
readonly commitHash: string;
readonly filePath: string;
}
export interface ResponseResetFileToRevision extends ResponseWithErrorInfo {
readonly command: 'resetFileToRevision';
}

export interface RequestResetToCommit extends RepoRequest {
readonly command: 'resetToCommit';
readonly commit: string;
Expand Down Expand Up @@ -1276,6 +1285,7 @@ export type RequestMessage =
| RequestRebase
| RequestRenameBranch
| RequestRescanForRepos
| RequestResetFileToRevision
| RequestResetToCommit
| RequestRevertCommit
| RequestSetGlobalViewState
Expand Down Expand Up @@ -1338,6 +1348,7 @@ export type ResponseMessage =
| ResponseRebase
| ResponseRefresh
| ResponseRenameBranch
| ResponseResetFileToRevision
| ResponseResetToCommit
| ResponseRevertCommit
| ResponseSetGlobalViewState
Expand Down
25 changes: 25 additions & 0 deletions tests/dataSource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6405,6 +6405,31 @@ describe('DataSource', () => {
});
});

describe('resetFileToRevision', () => {
it('Should reset file to revision', async () => {
// Setup
mockGitSuccessOnce();

// Run
const result = await dataSource.resetFileToRevision('/path/to/repo', '1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b', 'path/to/file');

// Assert
expect(result).toBe(null);
expect(spyOnSpawn).toBeCalledWith('/path/to/git', ['checkout', '1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b', '--', 'path/to/file'], expect.objectContaining({ cwd: '/path/to/repo' }));
});

it('Should return an error message thrown by git', async () => {
// Setup
mockGitThrowingErrorOnce();

// Run
const result = await dataSource.resetFileToRevision('/path/to/repo', '1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b', 'path/to/file');

// Assert
expect(result).toBe('error message');
});
});

describe('applyStash', () => {
it('Should apply a stash', async () => {
// Setup
Expand Down
28 changes: 28 additions & 0 deletions tests/gitGraphView.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3117,6 +3117,34 @@ describe('GitGraphView', () => {
});
});

describe('resetFileToRevision', () => {
it('Should reset the file to a revision', async () => {
// Setup
const resetFileToRevisionResolvedValue = null;
const spyOnResetFileToRevision = jest.spyOn(dataSource, 'resetFileToRevision');
spyOnResetFileToRevision.mockResolvedValueOnce(resetFileToRevisionResolvedValue);

// Run
onDidReceiveMessage({
command: 'resetFileToRevision',
repo: '/path/to/repo',
commitHash: '1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b',
filePath: 'path/to/file'
});

// Assert
await waitForExpect(() => {
expect(spyOnResetFileToRevision).toHaveBeenCalledWith('/path/to/repo', '1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b', 'path/to/file');
expect(messages).toStrictEqual([
{
command: 'resetFileToRevision',
error: resetFileToRevisionResolvedValue
}
]);
});
});
});

describe('resetToCommit', () => {
it('Should reset the current branch to a commit', async () => {
// Setup
Expand Down
27 changes: 26 additions & 1 deletion web/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2927,6 +2927,20 @@ class GitGraphView {
sendMessage({ command: 'copyFilePath', repo: this.currentRepo, filePath: file.newFilePath, absolute: absolute });
};

const triggerResetFileToRevision = (file: GG.GitFileChange, fileElem: HTMLElement) => {
const expandedCommit = this.expandedCommit;
if (expandedCommit === null) return;

const commitHash = getCommitHashForFile(file, expandedCommit);
dialog.showConfirmation('Are you sure you want to reset <b><i>' + escapeHtml(file.newFilePath) + '</i></b> to it\'s state at commit <b><i>' + abbrevCommit(commitHash) + '</i></b>? Any uncommitted changes made to this file will be overwritten.', 'Yes, reset file', () => {
runAction({ command: 'resetFileToRevision', repo: this.currentRepo, commitHash: commitHash, filePath: file.newFilePath }, 'Resetting file');
}, {
type: TargetType.CommitDetailsView,
hash: commitHash,
elem: fileElem
});
};

const triggerViewFileAtRevision = (file: GG.GitFileChange, fileElem: HTMLElement) => {
const expandedCommit = this.expandedCommit;
if (expandedCommit === null) return;
Expand Down Expand Up @@ -3024,7 +3038,8 @@ class GitGraphView {
elem: fileElem
};
const diffPossible = file.type === GG.GitFileStatus.Untracked || (file.additions !== null && file.deletions !== null);
const fileExistsAtThisRevisionAndDiffPossible = file.type !== GG.GitFileStatus.Deleted && diffPossible && !isUncommitted;
const fileExistsAtThisRevision = file.type !== GG.GitFileStatus.Deleted && !isUncommitted;
const fileExistsAtThisRevisionAndDiffPossible = fileExistsAtThisRevision && diffPossible;
const codeReviewInProgressAndNotReviewed = expandedCommit.codeReview !== null && expandedCommit.codeReview.remainingFiles.includes(file.newFilePath);

contextMenu.show([
Expand Down Expand Up @@ -3062,6 +3077,13 @@ class GitGraphView {
onClick: () => this.cdvUpdateFileState(file, fileElem, false, false)
}
],
[
{
title: 'Reset File to this Revision' + ELLIPSIS,
visible: fileExistsAtThisRevision && expandedCommit.compareWithHash === null,
onClick: () => triggerResetFileToRevision(file, fileElem)
}
],
[
{
title: 'Copy Absolute File Path to Clipboard',
Expand Down Expand Up @@ -3343,6 +3365,9 @@ window.addEventListener('load', () => {
case 'renameBranch':
refreshOrDisplayError(msg.error, 'Unable to Rename Branch');
break;
case 'resetFileToRevision':
refreshOrDisplayError(msg.error, 'Unable to Reset File to Revision');
break;
case 'resetToCommit':
refreshOrDisplayError(msg.error, 'Unable to Reset to Commit');
break;
Expand Down
4 changes: 2 additions & 2 deletions web/styles/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ code{
.fileTreeFileAction.viewGitFileAtRevision, .fileTreeFileAction.openGitFile{
margin-left:6px;
}
.fileTreeFileRecord:hover .fileTreeFileAction, .fileTreeFileRecord.contextMenuActive .fileTreeFileAction{
.fileTreeFileRecord:hover .fileTreeFileAction, .fileTreeFileRecord.contextMenuActive .fileTreeFileAction, .fileTreeFileRecord.dialogActive .fileTreeFileAction{
cursor:pointer;
opacity:1;
}
Expand All @@ -583,7 +583,7 @@ svg.openFolderIcon, svg.closedFolderIcon, svg.fileIcon, .fileTreeFileAction svg,
.fileTreeFileAction.viewGitFileAtRevision svg, .fileTreeFileAction.openGitFile svg{
top:4px;
}
.fileTreeFolder:hover svg.openFolderIcon, .fileTreeFolder:hover svg.closedFolderIcon, .fileTreeFileRecord:hover svg.fileIcon, .fileTreeFileRecord.contextMenuActive svg.fileIcon, .fileTreeFileRecord:hover #cdvLastFileViewed svg, .fileTreeFileRecord.contextMenuActive #cdvLastFileViewed svg, .fileTreeRepo:hover svg, .fileTreeFileAction:hover svg{
.fileTreeFolder:hover svg.openFolderIcon, .fileTreeFolder:hover svg.closedFolderIcon, .fileTreeFileRecord:hover svg.fileIcon, .fileTreeFileRecord.contextMenuActive svg.fileIcon, .fileTreeFileRecord.dialogActive svg.fileIcon, .fileTreeFileRecord:hover #cdvLastFileViewed svg, .fileTreeFileRecord.contextMenuActive #cdvLastFileViewed svg, .fileTreeFileRecord.dialogActive #cdvLastFileViewed svg, .fileTreeRepo:hover svg, .fileTreeFileAction:hover svg{
fill-opacity:0.8;
}
svg.openFolderIcon, svg.closedFolderIcon, svg.fileIcon{
Expand Down

0 comments on commit a50bafd

Please sign in to comment.