mirror of
https://github.com/microsoft/vscode.git
synced 2026-03-02 14:58:43 +00:00
cleanup git relative path calculation
This commit is contained in:
@@ -337,6 +337,7 @@ export const GitErrorCodes = {
|
||||
LocalChangesOverwritten: 'LocalChangesOverwritten',
|
||||
NoUpstreamBranch: 'NoUpstreamBranch',
|
||||
IsInSubmodule: 'IsInSubmodule',
|
||||
WrongCase: 'WrongCase',
|
||||
};
|
||||
|
||||
function getGitErrorCode(stderr: string): string | undefined {
|
||||
@@ -642,6 +643,36 @@ export function parseGitCommit(raw: string): Commit | null {
|
||||
return { hash: match[1], message: match[3], parents };
|
||||
}
|
||||
|
||||
interface LsTreeElement {
|
||||
mode: string;
|
||||
type: string;
|
||||
object: string;
|
||||
file: string;
|
||||
}
|
||||
|
||||
export function parseLsTree(raw: string): LsTreeElement[] {
|
||||
return raw.split('\n')
|
||||
.filter(l => !!l)
|
||||
.map(line => /^(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/.exec(line)!)
|
||||
.filter(m => !!m)
|
||||
.map(([, mode, type, object, file]) => ({ mode, type, object, file }));
|
||||
}
|
||||
|
||||
interface LsFilesElement {
|
||||
mode: string;
|
||||
object: string;
|
||||
stage: string;
|
||||
file: string;
|
||||
}
|
||||
|
||||
export function parseLsFiles(raw: string): LsFilesElement[] {
|
||||
return raw.split('\n')
|
||||
.filter(l => !!l)
|
||||
.map(line => /^(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/.exec(line)!)
|
||||
.filter(m => !!m)
|
||||
.map(([, mode, object, stage, file]) => ({ mode, object, stage, file }));
|
||||
}
|
||||
|
||||
export interface DiffOptions {
|
||||
cached?: boolean;
|
||||
}
|
||||
@@ -710,13 +741,19 @@ export class Repository {
|
||||
return Promise.reject<Buffer>('Can\'t open file from git');
|
||||
}
|
||||
|
||||
const { exitCode, stdout } = await exec(child);
|
||||
const { exitCode, stdout, stderr } = await exec(child);
|
||||
|
||||
if (exitCode) {
|
||||
return Promise.reject<Buffer>(new GitError({
|
||||
const err = new GitError({
|
||||
message: 'Could not show object.',
|
||||
exitCode
|
||||
}));
|
||||
});
|
||||
|
||||
if (/exists on disk, but not in/.test(stderr)) {
|
||||
err.gitErrorCode = GitErrorCodes.WrongCase;
|
||||
}
|
||||
|
||||
return Promise.reject<Buffer>(err);
|
||||
}
|
||||
|
||||
return stdout;
|
||||
@@ -751,25 +788,27 @@ export class Repository {
|
||||
return { mode, object, size: parseInt(size) };
|
||||
}
|
||||
|
||||
async lstreeOutput(treeish: string, path: string): Promise<string> {
|
||||
if (!treeish) { // index
|
||||
const { stdout } = await this.run(['ls-files', '--stage', '--', path]);
|
||||
return stdout;
|
||||
}
|
||||
|
||||
const { stdout } = await this.run(['ls-tree', '-l', treeish, '--', path]);
|
||||
return stdout;
|
||||
async lstree2(treeish: string, path: string): Promise<LsTreeElement[]> {
|
||||
const { stdout } = await this.run(['ls-tree', treeish, '--', path]);
|
||||
return parseLsTree(stdout);
|
||||
}
|
||||
|
||||
async relativePathToGitRelativePath(treeish: string, path: string): Promise<string> {
|
||||
let gitPath: string = path;
|
||||
const pathPrefix = path.substring(0, path.lastIndexOf('/'));
|
||||
const lstOutput = await this.lstreeOutput(treeish, pathPrefix + '/');
|
||||
const findResult = lstOutput.toUpperCase().indexOf(path.toUpperCase());
|
||||
if (findResult) {
|
||||
gitPath = lstOutput.substr(findResult, path.length);
|
||||
async lsfiles(path: string): Promise<LsFilesElement[]> {
|
||||
const { stdout } = await this.run(['ls-files', '--stage', '--', path]);
|
||||
return parseLsFiles(stdout);
|
||||
}
|
||||
|
||||
async getGitRelativePath(treeish: string, relativePath: string): Promise<string> {
|
||||
const relativePathLowercase = relativePath.toLowerCase();
|
||||
const dirname = path.posix.dirname(relativePath) + '/';
|
||||
const elements: { file: string; }[] = treeish ? await this.lstree2(treeish, dirname) : await this.lsfiles(dirname);
|
||||
const element = elements.filter(file => file.file.toLowerCase() === relativePathLowercase)[0];
|
||||
|
||||
if (!element) {
|
||||
throw new GitError({ message: 'Git relative path not found.' });
|
||||
}
|
||||
return gitPath;
|
||||
|
||||
return element.file;
|
||||
}
|
||||
|
||||
async detectObjectType(object: string): Promise<{ mimetype: string, encoding?: string }> {
|
||||
|
||||
@@ -876,8 +876,8 @@ export class Repository implements Disposable {
|
||||
}
|
||||
|
||||
async show(ref: string, filePath: string): Promise<string> {
|
||||
return this.run(Operation.Show, async () => {
|
||||
let relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/');
|
||||
return await this.run(Operation.Show, async () => {
|
||||
const relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/');
|
||||
const configFiles = workspace.getConfiguration('files', Uri.file(filePath));
|
||||
const defaultEncoding = configFiles.get<string>('encoding');
|
||||
const autoGuessEncoding = configFiles.get<boolean>('autoGuessEncoding');
|
||||
@@ -886,8 +886,16 @@ export class Repository implements Disposable {
|
||||
ref = 'HEAD';
|
||||
}
|
||||
|
||||
relativePath = await this.repository.relativePathToGitRelativePath(ref, relativePath);
|
||||
return this.repository.bufferString(`${ref}:${relativePath}`, defaultEncoding, autoGuessEncoding);
|
||||
try {
|
||||
return await this.repository.bufferString(`${ref}:${relativePath}`, defaultEncoding, autoGuessEncoding);
|
||||
} catch (err) {
|
||||
if (err.gitErrorCode === GitErrorCodes.WrongCase) {
|
||||
const gitRelativePath = await this.repository.getGitRelativePath(ref, relativePath);
|
||||
return await this.repository.bufferString(`${ref}:${gitRelativePath}`, defaultEncoding, autoGuessEncoding);
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
'use strict';
|
||||
|
||||
import 'mocha';
|
||||
import { GitStatusParser, parseGitCommit, parseGitmodules } from '../git';
|
||||
import { GitStatusParser, parseGitCommit, parseGitmodules, parseLsTree, parseLsFiles } from '../git';
|
||||
import * as assert from 'assert';
|
||||
|
||||
suite('git', () => {
|
||||
@@ -177,7 +177,7 @@ suite('git', () => {
|
||||
});
|
||||
|
||||
suite('parseGitCommit', () => {
|
||||
test('single parent commit', () => {
|
||||
test('single parent commit', function () {
|
||||
const GIT_OUTPUT_SINGLE_PARENT = `52c293a05038d865604c2284aa8698bd087915a1
|
||||
8e5a374372b8393906c7e380dbb09349c5385554
|
||||
This is a commit message.`;
|
||||
@@ -189,7 +189,7 @@ This is a commit message.`;
|
||||
});
|
||||
});
|
||||
|
||||
test('multiple parent commits', () => {
|
||||
test('multiple parent commits', function () {
|
||||
const GIT_OUTPUT_MULTIPLE_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1
|
||||
8e5a374372b8393906c7e380dbb09349c5385554 df27d8c75b129ab9b178b386077da2822101b217
|
||||
This is a commit message.`;
|
||||
@@ -201,7 +201,7 @@ This is a commit message.`;
|
||||
});
|
||||
});
|
||||
|
||||
test('no parent commits', async () => {
|
||||
test('no parent commits', function () {
|
||||
const GIT_OUTPUT_NO_PARENTS = `52c293a05038d865604c2284aa8698bd087915a1
|
||||
|
||||
This is a commit message.`;
|
||||
@@ -213,4 +213,68 @@ This is a commit message.`;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('parseLsTree', function () {
|
||||
test('sample', function () {
|
||||
const input = `040000 tree 0274a81f8ee9ca3669295dc40f510bd2021d0043 .vscode
|
||||
100644 blob 1d487c1817262e4f20efbfa1d04c18f51b0046f6 Screen Shot 2018-06-01 at 14.48.05.png
|
||||
100644 blob 686c16e4f019b734655a2576ce8b98749a9ffdb9 Screen Shot 2018-06-07 at 20.04.59.png
|
||||
100644 blob 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 boom.txt
|
||||
100644 blob 86dc360dd25f13fa50ffdc8259e9653921f4f2b7 boomcaboom.txt
|
||||
100644 blob a68b14060589b16d7ac75f67b905c918c03c06eb file.js
|
||||
100644 blob f7bcfb05af46850d780f88c069edcd57481d822d file.md
|
||||
100644 blob ab8b86114a051f6490f1ec5e3141b9a632fb46b5 hello.js
|
||||
100644 blob 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 what.js
|
||||
100644 blob be859e3f412fa86513cd8bebe8189d1ea1a3e46d what.txt
|
||||
100644 blob 56ec42c9dc6fcf4534788f0fe34b36e09f37d085 what.txt2`;
|
||||
|
||||
const output = parseLsTree(input);
|
||||
|
||||
assert.deepEqual(output, [
|
||||
{ mode: '040000', type: 'tree', object: '0274a81f8ee9ca3669295dc40f510bd2021d0043', file: '.vscode' },
|
||||
{ mode: '100644', type: 'blob', object: '1d487c1817262e4f20efbfa1d04c18f51b0046f6', file: 'Screen Shot 2018-06-01 at 14.48.05.png' },
|
||||
{ mode: '100644', type: 'blob', object: '686c16e4f019b734655a2576ce8b98749a9ffdb9', file: 'Screen Shot 2018-06-07 at 20.04.59.png' },
|
||||
{ mode: '100644', type: 'blob', object: '257cc5642cb1a054f08cc83f2d943e56fd3ebe99', file: 'boom.txt' },
|
||||
{ mode: '100644', type: 'blob', object: '86dc360dd25f13fa50ffdc8259e9653921f4f2b7', file: 'boomcaboom.txt' },
|
||||
{ mode: '100644', type: 'blob', object: 'a68b14060589b16d7ac75f67b905c918c03c06eb', file: 'file.js' },
|
||||
{ mode: '100644', type: 'blob', object: 'f7bcfb05af46850d780f88c069edcd57481d822d', file: 'file.md' },
|
||||
{ mode: '100644', type: 'blob', object: 'ab8b86114a051f6490f1ec5e3141b9a632fb46b5', file: 'hello.js' },
|
||||
{ mode: '100644', type: 'blob', object: '257cc5642cb1a054f08cc83f2d943e56fd3ebe99', file: 'what.js' },
|
||||
{ mode: '100644', type: 'blob', object: 'be859e3f412fa86513cd8bebe8189d1ea1a3e46d', file: 'what.txt' },
|
||||
{ mode: '100644', type: 'blob', object: '56ec42c9dc6fcf4534788f0fe34b36e09f37d085', file: 'what.txt2' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
suite('parseLsFiles', function () {
|
||||
test('sample', function () {
|
||||
const input = `100644 7a73a41bfdf76d6f793007240d80983a52f15f97 0 .vscode/settings.json
|
||||
100644 1d487c1817262e4f20efbfa1d04c18f51b0046f6 0 Screen Shot 2018-06-01 at 14.48.05.png
|
||||
100644 686c16e4f019b734655a2576ce8b98749a9ffdb9 0 Screen Shot 2018-06-07 at 20.04.59.png
|
||||
100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 0 boom.txt
|
||||
100644 86dc360dd25f13fa50ffdc8259e9653921f4f2b7 0 boomcaboom.txt
|
||||
100644 a68b14060589b16d7ac75f67b905c918c03c06eb 0 file.js
|
||||
100644 f7bcfb05af46850d780f88c069edcd57481d822d 0 file.md
|
||||
100644 ab8b86114a051f6490f1ec5e3141b9a632fb46b5 0 hello.js
|
||||
100644 257cc5642cb1a054f08cc83f2d943e56fd3ebe99 0 what.js
|
||||
100644 be859e3f412fa86513cd8bebe8189d1ea1a3e46d 0 what.txt
|
||||
100644 56ec42c9dc6fcf4534788f0fe34b36e09f37d085 0 what.txt2`;
|
||||
|
||||
const output = parseLsFiles(input);
|
||||
|
||||
assert.deepEqual(output, [
|
||||
{ mode: '100644', object: '7a73a41bfdf76d6f793007240d80983a52f15f97', stage: '0', file: '.vscode/settings.json' },
|
||||
{ mode: '100644', object: '1d487c1817262e4f20efbfa1d04c18f51b0046f6', stage: '0', file: 'Screen Shot 2018-06-01 at 14.48.05.png' },
|
||||
{ mode: '100644', object: '686c16e4f019b734655a2576ce8b98749a9ffdb9', stage: '0', file: 'Screen Shot 2018-06-07 at 20.04.59.png' },
|
||||
{ mode: '100644', object: '257cc5642cb1a054f08cc83f2d943e56fd3ebe99', stage: '0', file: 'boom.txt' },
|
||||
{ mode: '100644', object: '86dc360dd25f13fa50ffdc8259e9653921f4f2b7', stage: '0', file: 'boomcaboom.txt' },
|
||||
{ mode: '100644', object: 'a68b14060589b16d7ac75f67b905c918c03c06eb', stage: '0', file: 'file.js' },
|
||||
{ mode: '100644', object: 'f7bcfb05af46850d780f88c069edcd57481d822d', stage: '0', file: 'file.md' },
|
||||
{ mode: '100644', object: 'ab8b86114a051f6490f1ec5e3141b9a632fb46b5', stage: '0', file: 'hello.js' },
|
||||
{ mode: '100644', object: '257cc5642cb1a054f08cc83f2d943e56fd3ebe99', stage: '0', file: 'what.js' },
|
||||
{ mode: '100644', object: 'be859e3f412fa86513cd8bebe8189d1ea1a3e46d', stage: '0', file: 'what.txt' },
|
||||
{ mode: '100644', object: '56ec42c9dc6fcf4534788f0fe34b36e09f37d085', stage: '0', file: 'what.txt2' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user