Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add GitCommitterIdentityHandler and change identity check #689

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions jupyterlab_git/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,16 +792,27 @@ async def checkout_all(self, top_repo_path):
return {"code": code, "command": " ".join(cmd), "message": error}
return {"code": code}

async def reset_author(self, top_repo_path):
"""
Reset committer identity in previous commit
"""
cmd = ["git", "commit", "--amend", "--reset-author", "--no-edit"]
code, _, error = await execute(cmd, cwd=top_repo_path)

if code != 0:
return {"code": code, "command": " ".join(cmd), "message": error}
return {"code": code}

async def commit(self, commit_msg, top_repo_path):
"""
Execute git commit <filename> command & return the result.
"""
cmd = ["git", "commit", "-m", commit_msg]
code, _, error = await execute(cmd, cwd=top_repo_path)
code, output, error = await execute(cmd, cwd=top_repo_path)

if code != 0:
return {"code": code, "command": " ".join(cmd), "message": error}
return {"code": code}
return {"code": code, "output": output}

async def pull(self, curr_fb_path, auth=None, cancel_on_conflict=False):
"""
Expand Down
17 changes: 17 additions & 0 deletions jupyterlab_git/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,22 @@ async def post(self):
self.finish(json.dumps(body))


class GitResetAuthorHandler(GitHandler):
"""
Handler for 'git commit --amend --reset-author --no-edit'.
"""

@web.authenticated
async def post(self):
data = self.get_json_body()
top_repo_path = data["top_repo_path"]
body = await self.git.reset_author(top_repo_path)

if body["code"] != 0:
self.set_status(500)
self.finish(json.dumps(body))


class GitCommitHandler(GitHandler):
"""
Handler for 'git commit -m <message>'. Commits files.
Expand Down Expand Up @@ -711,6 +727,7 @@ def setup_handlers(web_app):
("/git/changed_files", GitChangedFilesHandler),
("/git/checkout", GitCheckoutHandler),
("/git/clone", GitCloneHandler),
("/git/reset_author", GitResetAuthorHandler),
("/git/commit", GitCommitHandler),
("/git/config", GitConfigHandler),
("/git/delete_commit", GitDeleteCommitHandler),
Expand Down
86 changes: 38 additions & 48 deletions src/components/GitPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { PathExt } from '@jupyterlab/coreutils';
import { FileBrowserModel } from '@jupyterlab/filebrowser';
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { JSONObject } from '@lumino/coreutils';
import { GitExtension } from '../model';
import { sleep } from '../utils';
import { Git, ILogMessage } from '../tokens';
Expand All @@ -28,6 +27,9 @@ import { SuspendModal } from './SuspendModal';
import { Alert } from './Alert';
import { CommandIDs } from '../commandsAndMenu';

const MISSING_IDENTITY =
'Your name and email address were configured automatically';

/**
* Interface describing component properties.
*/
Expand Down Expand Up @@ -227,35 +229,20 @@ export class GitPanel extends React.Component<IGitPanelProps, IGitPanelState> {
* @returns a promise which commits the files
*/
commitStagedFiles = async (message: string): Promise<void> => {
let res: boolean;
let res: Response;
if (!message) {
return;
}
try {
res = await this._hasIdentity(this.props.model.pathRepository);
} catch (err) {
this._log({
severity: 'error',
message: 'Failed to commit changes.'
});
console.error(err);
showErrorMessage('Fail to commit', err);
return;
}
if (!res) {
this._log({
severity: 'error',
message: 'Failed to commit changes.'
});
return;
}
this._log({
severity: 'info',
message: 'Committing changes...'
});
this._suspend(true);
try {
await Promise.all([sleep(1000), this.props.model.commit(message)]);
[, res] = await Promise.all<any, Response>([
sleep(1000),
this.props.model.commit(message)
]);
} catch (err) {
this._suspend(false);
this._log({
Expand All @@ -271,6 +258,10 @@ export class GitPanel extends React.Component<IGitPanelProps, IGitPanelState> {
severity: 'success',
message: 'Committed changes.'
});
const { output } = await res.json();
if (output.indexOf(MISSING_IDENTITY) !== -1) {
await this._setIdentity(this.props.model.pathRepository);
}
};

/**
Expand Down Expand Up @@ -570,37 +561,36 @@ export class GitPanel extends React.Component<IGitPanelProps, IGitPanelState> {
* @param path - repository path
* @returns a promise which returns a success status
*/
private async _hasIdentity(path: string): Promise<boolean> {
private async _setIdentity(path: string): Promise<boolean> {
// If the repository path changes, check the user identity
if (path !== this._previousRepoPath) {
try {
let res = await this.props.model.config();
if (res.ok) {
const options: JSONObject = (await res.json()).options;
const keys = Object.keys(options);

// If the user name or e-mail is unknown, ask the user to set it
if (keys.indexOf('user.name') < 0 || keys.indexOf('user.email') < 0) {
const result = await showDialog({
title: 'Who is committing?',
body: new GitAuthorForm()
});
if (!result.button.accept) {
console.log('User refuses to set identity.');
return false;
}
const identity = result.value;
res = await this.props.model.config({
'user.name': identity.name,
'user.email': identity.email
});
if (!res.ok) {
console.log(await res.text());
return false;
}
}
this._previousRepoPath = path;
const result = await showDialog({
title: 'Who is committing?',
body: new GitAuthorForm()
});
if (!result.button.accept) {
console.log('User refuses to set identity.');
return false;
}
const identity = result.value;
let res = await this.props.model.config({
'user.name': identity.name,
'user.email': identity.email
});
if (!res.ok) {
console.log(await res.text());
return false;
}
this._suspend(true);
res = await this.props.model.resetAuthor();
if (!res.ok) {
this._suspend(false);
console.log(await res.text());
return false;
}
this._suspend(false);
this._previousRepoPath = path;
} catch (error) {
throw new Error('Failed to set your identity. ' + error.message);
}
Expand Down
34 changes: 34 additions & 0 deletions src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,40 @@ export class GitExtension implements IGitExtension {
return data;
}

async resetAuthor(): Promise<Response> {
await this.ready;
const path = this.pathRepository;

if (path === null) {
return Promise.resolve(
new Response(
JSON.stringify({
code: -1,
message: 'Not in a git repository.'
})
)
);
}

try {
const response = await httpGitRequest('/git/reset_author', 'POST', {
top_repo_path: path
});
if (!response.ok) {
return response.json().then((data: any) => {
throw new ServerConnection.ResponseError(response, data.message);
});
}

this.refreshStatus();
this._headChanged.emit();

return response;
} catch (err) {
throw new ServerConnection.NetworkError(err);
}
}

/**
* Commit all staged file changes.
*
Expand Down
Loading