Skip to content

Commit ff0099f

Browse files
authored
Fix Git History (#1028) (#1032)
* No Git history for selected file Fixes #1024 * No context menu item if not in a repository
1 parent 0907a0d commit ff0099f

File tree

4 files changed

+90
-38
lines changed

4 files changed

+90
-38
lines changed

jest.config.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,17 @@ module.exports = {
2222
preset: 'ts-jest/presets/js-with-babel',
2323
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
2424
modulePathIgnorePatterns: [
25+
'<rootDir>/build',
2526
'<rootDir>/jupyterlab_git',
2627
'<rootDir>/jupyter-config'
2728
],
2829
setupFiles: ['<rootDir>/testutils/jest-setup-files.js'],
29-
testPathIgnorePatterns: ['/lib/', '/node_modules/', '/jupyterlab_git/'],
30+
testPathIgnorePatterns: [
31+
'/lib/',
32+
'/node_modules/',
33+
'/jupyterlab_git/',
34+
'/ui-tests/'
35+
],
3036
testRegex: '/tests/.*.spec.ts[x]?$',
3137
transformIgnorePatterns: [`/node_modules/(?!${esModules}).+`],
3238
globals: {

src/commandsAndMenu.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1057,7 +1057,11 @@ export function addFileBrowserContextMenu(
10571057
const items = toArray(filebrowser.selectedItems());
10581058
const statuses = new Set<Git.Status>(
10591059
items
1060-
.map(item => model.getFile(item.path)?.status)
1060+
.map(item =>
1061+
model.pathRepository === null
1062+
? undefined
1063+
: model.getFile(item.path)?.status
1064+
)
10611065
.filter(status => typeof status !== 'undefined')
10621066
);
10631067

src/model.ts

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -294,24 +294,41 @@ export class GitExtension implements IGitExtension {
294294
/**
295295
* Match files status information based on a provided file path.
296296
*
297-
* If the file is tracked and has no changes, a StatusFile of unmodified will be returned
297+
* If the file is tracked and has no changes, a StatusFile of unmodified will be returned.
298298
*
299299
* @param path the file path relative to the server root
300+
* @returns The file status or null if path repository is null or path not in repository
300301
*/
301-
getFile(path: string): Git.IStatusFile {
302-
return (
303-
this._status.files.find(status => {
304-
return this.getRelativeFilePath(status.to) === path;
305-
}) ?? {
306-
x: '',
307-
y: '',
308-
to: path,
309-
from: '',
310-
is_binary: null,
311-
status: 'unmodified',
312-
type: this._resolveFileType(path)
302+
getFile(path: string): Git.IStatusFile | null {
303+
if (this.pathRepository === null) {
304+
return null;
305+
}
306+
const fileStatus = this._status.files.find(status => {
307+
return this.getRelativeFilePath(status.to) === path;
308+
});
309+
310+
if (!fileStatus) {
311+
const relativePath = PathExt.relative(
312+
'/' + this.pathRepository,
313+
'/' + path
314+
);
315+
316+
if (relativePath.startsWith('../')) {
317+
return null;
318+
} else {
319+
return {
320+
x: '',
321+
y: '',
322+
to: relativePath,
323+
from: '',
324+
is_binary: null,
325+
status: 'unmodified',
326+
type: this._resolveFileType(path)
327+
};
313328
}
314-
);
329+
} else {
330+
return fileStatus;
331+
}
315332
}
316333

317334
/**

tests/model.spec.tsx

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe('IGitExtension', () => {
2626
};
2727

2828
mockResponses = {
29-
responses: {...defaultMockedResponses}
29+
responses: { ...defaultMockedResponses }
3030
};
3131

3232
mockGit.requestAPI.mockImplementation(mockedRequestAPI(mockResponses));
@@ -85,7 +85,7 @@ describe('IGitExtension', () => {
8585
expect(model.isReady).toBe(false);
8686
});
8787

88-
mockResponses.path = DEFAULT_REPOSITORY_PATH
88+
mockResponses.path = DEFAULT_REPOSITORY_PATH;
8989
model.pathRepository = mockResponses.path;
9090
expect(model.isReady).toBe(false);
9191
await model.ready;
@@ -269,32 +269,57 @@ describe('IGitExtension', () => {
269269
});
270270
});
271271

272+
describe('#getFile', () => {
273+
it.each([
274+
['dir1/dir2/repo/somefolder/file', 'somefolder/file', 'dir1/dir2/repo'],
275+
['repo/file', 'file', 'repo'],
276+
['repo/somefolder/file', 'somefolder/file', 'repo'],
277+
['somefolder/file', 'somefolder/file', ''],
278+
['file', 'file', ''],
279+
['file', null, null],
280+
['other_repo/file', null, 'repo'],
281+
['root/other_repo/file', null, 'root/repo']
282+
])(
283+
'%s should return unmodified status with path relative to the repository',
284+
async (path, expected, repo) => {
285+
// Given
286+
mockResponses.path = repo;
287+
model.pathRepository = repo;
288+
await model.ready;
289+
// When
290+
const status = model.getFile(path);
291+
// Then
292+
if (expected === null) {
293+
expect(status).toBeNull();
294+
} else {
295+
expect(status.status).toEqual('unmodified');
296+
expect(status.to).toEqual(expected);
297+
}
298+
}
299+
);
300+
});
301+
272302
describe('#getRelativeFilePath', () => {
273-
it('should return relative path correctly ', async () => {
274-
const testData = [
275-
[
276-
'somefolder/file',
277-
'dir1/dir2/repo',
278-
'dir1/dir2/repo/somefolder/file'
279-
],
280-
['file', 'repo', 'repo/file'],
281-
['somefolder/file', 'repo', 'repo/somefolder/file'],
282-
['somefolder/file', '', 'somefolder/file'],
283-
['file', '', 'file'],
284-
['file', null, null]
285-
];
286-
287-
for (const testDatum of testData) {
303+
it.each([
304+
['somefolder/file', 'dir1/dir2/repo', 'dir1/dir2/repo/somefolder/file'],
305+
['file', 'repo', 'repo/file'],
306+
['somefolder/file', 'repo', 'repo/somefolder/file'],
307+
['somefolder/file', '', 'somefolder/file'],
308+
['file', '', 'file'],
309+
['file', null, null]
310+
])(
311+
'%s should return relative path correctly ',
312+
async (path, repo, expected) => {
288313
// Given
289-
mockResponses.path = testDatum[1];
290-
model.pathRepository = testDatum[1];
314+
mockResponses.path = repo;
315+
model.pathRepository = repo;
291316
await model.ready;
292317
// When
293-
const relativePath = model.getRelativeFilePath(testDatum[0]);
318+
const relativePath = model.getRelativeFilePath(path);
294319
// Then
295-
expect(relativePath).toEqual(testDatum[2]);
320+
expect(relativePath).toEqual(expected);
296321
}
297-
});
322+
);
298323
});
299324

300325
describe('#checkout', () => {

0 commit comments

Comments
 (0)