Skip to content

Commit

Permalink
Merge pull request #676 from ianhi/update-open-files
Browse files Browse the repository at this point in the history
Update open files when Git commands modify them
  • Loading branch information
fcollonval authored Aug 8, 2020
2 parents 7558e29 + 57b90ec commit 88acc03
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 17 deletions.
6 changes: 3 additions & 3 deletions jupyterlab_git/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ async def config(self, top_repo_path, **kwargs):

return response

async def changed_files(self, base=None, remote=None, single_commit=None):
async def changed_files(self, current_path, base=None, remote=None, single_commit=None):
"""Gets the list of changed files between two Git refs, or the files changed in a single commit
There are two reserved "refs" for the base
Expand All @@ -204,7 +204,7 @@ async def changed_files(self, base=None, remote=None, single_commit=None):
}
"""
if single_commit:
cmd = ["git", "diff", "{}^!".format(single_commit), "--name-only", "-z"]
cmd = ["git", "diff", single_commit, "--name-only", "-z"]
elif base and remote:
if base == "WORKING":
cmd = ["git", "diff", remote, "--name-only", "-z"]
Expand All @@ -219,7 +219,7 @@ async def changed_files(self, base=None, remote=None, single_commit=None):

response = {}
try:
code, output, error = await execute(cmd, cwd=self.root_dir)
code, output, error = await execute(cmd, cwd=os.path.join(self.root_dir, current_path))
except subprocess.CalledProcessError as e:
response["code"] = e.returncode
response["message"] = e.output.decode("utf-8")
Expand Down
22 changes: 11 additions & 11 deletions jupyterlab_git/tests/test_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
async def test_changed_files_invalid_input():
with pytest.raises(tornado.web.HTTPError):
await Git(FakeContentManager("/bin")).changed_files(
base="64950a634cd11d1a01ddfedaeffed67b531cb11e"
current_path="test-path", base="64950a634cd11d1a01ddfedaeffed67b531cb11e"
)


Expand All @@ -29,7 +29,7 @@ async def test_changed_files_single_commit():

# When
actual_response = await Git(FakeContentManager("/bin")).changed_files(
single_commit="64950a634cd11d1a01ddfedaeffed67b531cb11e"
current_path="test-path", single_commit="64950a634cd11d1a01ddfedaeffed67b531cb11e^!"
)

# Then
Expand All @@ -41,7 +41,7 @@ async def test_changed_files_single_commit():
"--name-only",
"-z",
],
cwd="/bin",
cwd="/bin/test-path",
)
assert {"code": 0, "files": ["file1.ipynb", "file2.py"]} == actual_response

Expand All @@ -56,12 +56,12 @@ async def test_changed_files_working_tree():

# When
actual_response = await Git(FakeContentManager("/bin")).changed_files(
base="WORKING", remote="HEAD"
current_path="test-path", base="WORKING", remote="HEAD"
)

# Then
mock_execute.assert_called_once_with(
["git", "diff", "HEAD", "--name-only", "-z"], cwd="/bin"
["git", "diff", "HEAD", "--name-only", "-z"], cwd="/bin/test-path"
)
assert {"code": 0, "files": ["file1.ipynb", "file2.py"]} == actual_response

Expand All @@ -76,12 +76,12 @@ async def test_changed_files_index():

# When
actual_response = await Git(FakeContentManager("/bin")).changed_files(
base="INDEX", remote="HEAD"
current_path="test-path", base="INDEX", remote="HEAD"
)

# Then
mock_execute.assert_called_once_with(
["git", "diff", "--staged", "HEAD", "--name-only", "-z"], cwd="/bin"
["git", "diff", "--staged", "HEAD", "--name-only", "-z"], cwd="/bin/test-path"
)
assert {"code": 0, "files": ["file1.ipynb", "file2.py"]} == actual_response

Expand All @@ -96,12 +96,12 @@ async def test_changed_files_two_commits():

# When
actual_response = await Git(FakeContentManager("/bin")).changed_files(
base="HEAD", remote="origin/HEAD"
current_path = "test-path", base="HEAD", remote="origin/HEAD"
)

# Then
mock_execute.assert_called_once_with(
["git", "diff", "HEAD", "origin/HEAD", "--name-only", "-z"], cwd="/bin"
["git", "diff", "HEAD", "origin/HEAD", "--name-only", "-z"], cwd="/bin/test-path"
)
assert {"code": 0, "files": ["file1.ipynb", "file2.py"]} == actual_response

Expand All @@ -114,12 +114,12 @@ async def test_changed_files_git_diff_error():

# When
actual_response = await Git(FakeContentManager("/bin")).changed_files(
base="HEAD", remote="origin/HEAD"
current_path="test-path", base="HEAD", remote="origin/HEAD"
)

# Then
mock_execute.assert_called_once_with(
["git", "diff", "HEAD", "origin/HEAD", "--name-only", "-z"], cwd="/bin"
["git", "diff", "HEAD", "origin/HEAD", "--name-only", "-z"], cwd="/bin/test-path"
)
assert {"code": 128, "message": "error message"} == actual_response

Expand Down
10 changes: 9 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from '@jupyterlab/application';
import { Dialog, showErrorMessage } from '@jupyterlab/apputils';
import { IChangedArgs } from '@jupyterlab/coreutils';
import { IDocumentManager } from '@jupyterlab/docmanager';
import { FileBrowserModel, IFileBrowserFactory } from '@jupyterlab/filebrowser';
import { IMainMenu } from '@jupyterlab/mainmenu';
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
Expand Down Expand Up @@ -32,6 +33,7 @@ const plugin: JupyterFrontEndPlugin<IGitExtension> = {
IFileBrowserFactory,
IRenderMimeRegistry,
ISettingRegistry,
IDocumentManager,
IStatusBar
],
provides: IGitExtension,
Expand All @@ -54,6 +56,7 @@ async function activate(
factory: IFileBrowserFactory,
renderMime: IRenderMimeRegistry,
settingRegistry: ISettingRegistry,
docmanager: IDocumentManager,
statusBar: IStatusBar
): Promise<IGitExtension> {
let gitExtension: GitExtension | null = null;
Expand Down Expand Up @@ -106,7 +109,12 @@ async function activate(
return null;
}
// Create the Git model
gitExtension = new GitExtension(serverSettings.serverRoot, app, settings);
gitExtension = new GitExtension(
serverSettings.serverRoot,
app,
docmanager,
settings
);

// Whenever we restore the application, sync the Git extension path
Promise.all([app.restored, filebrowser.model.restored]).then(() => {
Expand Down
94 changes: 94 additions & 0 deletions src/model.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { JupyterFrontEnd } from '@jupyterlab/application';
import { IChangedArgs, PathExt } from '@jupyterlab/coreutils';
import { IDocumentManager } from '@jupyterlab/docmanager';
import { ServerConnection } from '@jupyterlab/services';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { LinkedList } from '@lumino/collections';
Expand Down Expand Up @@ -28,11 +29,13 @@ export class GitExtension implements IGitExtension {
constructor(
serverRoot: string,
app: JupyterFrontEnd = null,
docmanager: IDocumentManager = null,
settings?: ISettingRegistry.ISettings
) {
const self = this;
this._serverRoot = serverRoot;
this._app = app;
this._docmanager = docmanager;
this._settings = settings || null;

let interval: number;
Expand Down Expand Up @@ -459,6 +462,20 @@ export class GitExtension implements IGitExtension {
const tid = this._addTask('git:checkout');
try {
response = await httpGitRequest('/git/checkout', 'POST', body);

if (response.ok) {
if (body.checkout_branch) {
const files = (await this.changedFiles(
this._currentBranch.name,
body.branchname
))['files'];
if (files) {
files.forEach(file => this._revertFile(file));
}
} else {
this._revertFile(options.filename);
}
}
} catch (err) {
throw new ServerConnection.NetworkError(err);
} finally {
Expand All @@ -468,6 +485,7 @@ export class GitExtension implements IGitExtension {
if (!response.ok) {
throw new ServerConnection.ResponseError(response, data.message);
}

if (body.checkout_branch) {
await this.refreshBranch();
this._headChanged.emit();
Expand Down Expand Up @@ -609,11 +627,17 @@ export class GitExtension implements IGitExtension {
return Promise.resolve(new Response(JSON.stringify(response)));
}
const tid = this._addTask('git:commit:revert');
const files = (await this.changedFiles(null, null, hash + '^!'))['files'];
try {
response = await httpGitRequest('/git/delete_commit', 'POST', {
commit_id: hash,
top_repo_path: path
});
if (response.ok && files) {
files.forEach(file => {
this._revertFile(file);
});
}
} catch (err) {
throw new ServerConnection.NetworkError(err);
} finally {
Expand Down Expand Up @@ -905,12 +929,29 @@ export class GitExtension implements IGitExtension {
return Promise.resolve(new Response(JSON.stringify(response)));
}
const tid = this._addTask('git:reset:changes');
const reset_all = filename === undefined;
let files;
if (reset_all) {
files = (await this.changedFiles('INDEX', 'HEAD'))['files'];
}
try {
response = await httpGitRequest('/git/reset', 'POST', {
reset_all: filename === undefined,
filename: filename === undefined ? null : filename,
top_repo_path: path
});

if (response.ok) {
if (reset_all) {
if (files) {
files.forEach(file => {
this._revertFile(file);
});
}
} else {
this._revertFile(filename);
}
}
} catch (err) {
throw new ServerConnection.NetworkError(err);
} finally {
Expand Down Expand Up @@ -947,12 +988,20 @@ export class GitExtension implements IGitExtension {
};
return Promise.resolve(new Response(JSON.stringify(response)));
}
const files = (await this.changedFiles(null, null, hash))['files'];
const tid = this._addTask('git:reset:hard');
try {
response = await httpGitRequest('/git/reset_to_commit', 'POST', {
commit_id: hash,
top_repo_path: path
});
if (response.ok) {
if (files) {
files.forEach(file => {
this._revertFile(file);
});
}
}
} catch (err) {
throw new ServerConnection.NetworkError(err);
} finally {
Expand Down Expand Up @@ -1226,6 +1275,37 @@ export class GitExtension implements IGitExtension {
return Promise.resolve(response);
}

/**
* Get list of files changed between two commits or two branches
* @param base id of base commit or base branch for comparison
* @param remote id of remote commit or remote branch for comparison
* @param singleCommit id of a single commit
*
* @returns the names of the changed files
*/
async changedFiles(
base?: string,
remote?: string,
singleCommit?: string
): Promise<Git.IChangedFilesResult> {
try {
const response = await httpGitRequest('/git/changed_files', 'POST', {
current_path: this.pathRepository,
base: base,
remote: remote,
single_commit: singleCommit
});
if (!response.ok) {
return response.json().then((data: any) => {
throw new ServerConnection.ResponseError(response, data.message);
});
}
return response.json();
} catch (err) {
throw new ServerConnection.NetworkError(err);
}
}

/**
* Make request for a list of all git branches in the repository
* Retrieve a list of repository branches.
Expand Down Expand Up @@ -1344,12 +1424,26 @@ export class GitExtension implements IGitExtension {
return this._taskID;
}

/**
* if file is open in JupyterLab find the widget and ensure the JupyterLab
* version matches the version on disk. Do nothing if the file has unsaved changes
*
* @param path path to the file to be reverted
*/
private _revertFile(path: string): void {
const widget = this._docmanager.findWidget(this.getRelativeFilePath(path));
if (widget && !widget.context.model.dirty) {
widget.context.revert();
}
}

private _status: Git.IStatusFile[] = [];
private _pathRepository: string | null = null;
private _branches: Git.IBranch[];
private _currentBranch: Git.IBranch;
private _serverRoot: string;
private _app: JupyterFrontEnd | null;
private _docmanager: IDocumentManager | null;
private _diffProviders: { [key: string]: Git.IDiffCallback } = {};
private _isDisposed = false;
private _markerCache: Markers = new Markers(() => this._markChanged.emit());
Expand Down
9 changes: 9 additions & 0 deletions src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,15 @@ export namespace Git {
files?: IStatusFileResult[];
}

/** Interface for changed_files request result
* lists the names of files that have differences between two commits
* or beween two branches, or that were changed by a single commit
*/
export interface IChangedFilesResult {
code: number;
files?: string[];
}

/** Interface for GitLog request result,
* has the info of a single past commit
*/
Expand Down
Loading

0 comments on commit 88acc03

Please sign in to comment.