Skip to content

Commit 97b8727

Browse files
authored
Git - fix viewing stahes with added/deleted/renamed files (microsoft#203341)
1 parent 25af41d commit 97b8727

File tree

3 files changed

+84
-12
lines changed

3 files changed

+84
-12
lines changed

extensions/git/src/commands.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3708,19 +3708,26 @@ export class CommandCenter {
37083708
return;
37093709
}
37103710

3711-
const stashFiles = await repository.showStash(stash.index);
3711+
const stashChanges = await repository.showStash(stash.index);
37123712

3713-
if (!stashFiles || stashFiles.length === 0) {
3713+
if (!stashChanges || stashChanges.length === 0) {
37143714
return;
37153715
}
37163716

37173717
const title = `Git Stash #${stash.index}: ${stash.description}`;
37183718
const multiDiffSourceUri = toGitUri(Uri.file(repository.root), `stash@{${stash.index}}`, { scheme: 'git-stash' });
37193719

3720-
const resources: { originalUri: Uri; modifiedUri: Uri }[] = [];
3721-
for (const file of stashFiles) {
3722-
const fileUri = Uri.file(path.join(repository.root, file));
3723-
resources.push({ originalUri: fileUri, modifiedUri: toGitUri(fileUri, `stash@{${stash.index}}`) });
3720+
const resources: { originalUri: Uri | undefined; modifiedUri: Uri | undefined }[] = [];
3721+
for (const change of stashChanges) {
3722+
if (change.status === Status.INDEX_ADDED) {
3723+
resources.push({ originalUri: undefined, modifiedUri: toGitUri(change.uri, `stash@{${stash.index}}`) });
3724+
} else if (change.status === Status.DELETED) {
3725+
resources.push({ originalUri: change.uri, modifiedUri: undefined });
3726+
} else if (change.status === Status.INDEX_RENAMED) {
3727+
resources.push({ originalUri: change.originalUri, modifiedUri: toGitUri(change.uri, `stash@{${stash.index}}`) });
3728+
} else {
3729+
resources.push({ originalUri: change.uri, modifiedUri: toGitUri(change.uri, `stash@{${stash.index}}`) });
3730+
}
37243731
}
37253732

37263733
commands.executeCommand('_workbench.openMultiDiffEditor', { multiDiffSourceUri, title, resources });

extensions/git/src/git.ts

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,70 @@ function parseGitStashes(raw: string): Stash[] {
997997
return result;
998998
}
999999

1000+
// TODO@lszomoru - adopt in diffFiles()
1001+
function parseGitChanges(repositoryRoot: string, raw: string): Change[] {
1002+
let index = 0;
1003+
const result: Change[] = [];
1004+
const segments = raw.trim()
1005+
.split('\x00')
1006+
.filter(s => s);
1007+
1008+
segmentsLoop:
1009+
while (index < segments.length - 1) {
1010+
const change = segments[index++];
1011+
const resourcePath = segments[index++];
1012+
1013+
if (!change || !resourcePath) {
1014+
break;
1015+
}
1016+
1017+
const originalUri = Uri.file(path.isAbsolute(resourcePath) ? resourcePath : path.join(repositoryRoot, resourcePath));
1018+
1019+
let uri = originalUri;
1020+
let renameUri = originalUri;
1021+
let status = Status.UNTRACKED;
1022+
1023+
// Copy or Rename status comes with a number (ex: 'R100').
1024+
// We don't need the number, we use only first character of the status.
1025+
switch (change[0]) {
1026+
case 'A':
1027+
status = Status.INDEX_ADDED;
1028+
break;
1029+
1030+
case 'M':
1031+
status = Status.MODIFIED;
1032+
break;
1033+
1034+
case 'D':
1035+
status = Status.DELETED;
1036+
break;
1037+
1038+
// Rename contains two paths, the second one is what the file is renamed/copied to.
1039+
case 'R': {
1040+
if (index >= segments.length) {
1041+
break;
1042+
}
1043+
1044+
const newPath = segments[index++];
1045+
if (!newPath) {
1046+
break;
1047+
}
1048+
1049+
status = Status.INDEX_RENAMED;
1050+
uri = renameUri = Uri.file(path.isAbsolute(newPath) ? newPath : path.join(repositoryRoot, newPath));
1051+
break;
1052+
}
1053+
default:
1054+
// Unknown status
1055+
break segmentsLoop;
1056+
}
1057+
1058+
result.push({ status, uri, originalUri, renameUri });
1059+
}
1060+
1061+
return result;
1062+
}
1063+
10001064
export interface PullOptions {
10011065
unshallow?: boolean;
10021066
tags?: boolean;
@@ -2147,15 +2211,16 @@ export class Repository {
21472211
}
21482212
}
21492213

2150-
async showStash(index: number): Promise<string[] | undefined> {
2151-
const args = ['stash', 'show', `stash@{${index}}`, '--name-only'];
2214+
async showStash(index: number): Promise<Change[] | undefined> {
2215+
const args = ['stash', 'show', `stash@{${index}}`, '--name-status', '-z'];
21522216

21532217
try {
21542218
const result = await this.exec(args);
2219+
if (result.exitCode) {
2220+
return [];
2221+
}
21552222

2156-
return result.stdout.trim()
2157-
.split('\n')
2158-
.filter(line => !!line);
2223+
return parseGitChanges(this.repositoryRoot, result.stdout.trim());
21592224
} catch (err) {
21602225
if (/No stash found/.test(err.stderr || '')) {
21612226
return undefined;

extensions/git/src/repository.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1995,7 +1995,7 @@ export class Repository implements Disposable {
19951995
return await this.run(Operation.Stash, () => this.repository.applyStash(index));
19961996
}
19971997

1998-
async showStash(index: number): Promise<string[] | undefined> {
1998+
async showStash(index: number): Promise<Change[] | undefined> {
19991999
return await this.run(Operation.Stash, () => this.repository.showStash(index));
20002000
}
20012001

0 commit comments

Comments
 (0)