Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Implement ui to support multiple remotes #1146

Merged
merged 30 commits into from
Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
261c15e
implement name-url pair for adding remote repo
BoscoCHW Jul 7, 2022
c1e6e90
implement name-url pair for adding remote repo
BoscoCHW Jul 7, 2022
75c9628
Merge branch 'issue-234' of github.com:BoscoCHW/jupyterlab-git into i…
BoscoCHW Jul 11, 2022
2b0987b
Style Add Remote form
BoscoCHW Jul 11, 2022
b40ba77
show existing remote on add remote form
BoscoCHW Jul 12, 2022
ae6785f
Implement backend api to show remote url info
BoscoCHW Jul 13, 2022
f2287fd
Refactor remote_show function to handle verbose case
BoscoCHW Jul 13, 2022
5af1c3b
Implement remote dialog box using React
BoscoCHW Jul 14, 2022
eda196a
Style remote dialog
BoscoCHW Jul 14, 2022
fd7451a
Add remove remote button
BoscoCHW Jul 14, 2022
f6b1da6
Implement backend to remove remote
BoscoCHW Jul 15, 2022
7b205d9
Document codes
BoscoCHW Jul 15, 2022
b8588a9
Show push remote url only
BoscoCHW Jul 18, 2022
c009874
Implement pushing options for multiple remotes
BoscoCHW Jul 18, 2022
1f7bac9
Change GitRemoteDetailsShowHandler successful code
BoscoCHW Jul 21, 2022
b085bbc
Attempting to implement the DELETE method for removing a remote
BoscoCHW Jul 21, 2022
afb6e9e
style existing remote list with the grid api
BoscoCHW Jul 22, 2022
99df0f3
Fix git remote remove route bug
BoscoCHW Jul 22, 2022
5e505c0
Implement advanced push dialog box
BoscoCHW Jul 22, 2022
f077fc0
Show remote url in advanced push dialog and increase text font size
BoscoCHW Jul 22, 2022
dd214d7
Move dialog action buttons to just below input fields and display mes…
BoscoCHW Jul 29, 2022
30fdc42
Display loading message when getting remote information and handle no…
BoscoCHW Jul 29, 2022
386bf3b
Add tests for remote_show and remote_remove
BoscoCHW Aug 2, 2022
94f3f10
Remove cancel button from add remote dialog
BoscoCHW Aug 3, 2022
651ef2e
Change command gitAddRemote to gitManageRemote
BoscoCHW Aug 3, 2022
9f3ee72
Rename files to reflect command name 'ManageRemote'
BoscoCHW Aug 3, 2022
ecff43a
Refactor manageRemote command to let the dialog handle the adding and…
BoscoCHW Aug 3, 2022
1618609
Comment out tests for addRemote command
BoscoCHW Aug 3, 2022
9c686a4
Remove test for git:add-remote command
BoscoCHW Aug 5, 2022
c799be3
Add tests for component 'ManageRemoteDialogue'
BoscoCHW Aug 9, 2022
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
40 changes: 35 additions & 5 deletions jupyterlab_git/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -1494,24 +1494,54 @@ async def remote_add(self, path, url, name=DEFAULT_REMOTE_NAME):

return response

async def remote_show(self, path):
async def remote_show(self, path, verbose=False):
"""Handle call to `git remote show` command.
Args:
path (str): Git repository path

verbose (bool): true if details are needed, otherwise, false
Returns:
List[str]: Known remotes
if not verbose: List[str]: Known remotes
if verbose: List[ { name: str, url: str } ]: Known remotes
"""
command = ["git", "remote", "show"]
command = ["git", "remote"]
if verbose:
command.extend(["-v", "show"])
else:
command.append("show")

code, output, error = await execute(command, cwd=path)
response = {"code": code, "command": " ".join(command)}

if code == 0:
response["remotes"] = [r.strip() for r in output.splitlines()]
if verbose:
response["remotes"] = [
{"name": r.split("\t")[0], "url": r.split("\t")[1][:-7]}
for r in output.splitlines()
if "(push)" in r
]
else:
response["remotes"] = [r.strip() for r in output.splitlines()]
else:
response["message"] = error

return response

async def remote_remove(self, path, name):
"""Handle call to `git remote remove <name>` command.
Args:
path (str): Git repository path
name (str): Remote name
"""
command = ["git", "remote", "remove", name]

code, _, error = await execute(command, cwd=path)
response = {"code": code, "command": " ".join(command)}

if code != 0:
response["message"] = error

return response

async def ensure_gitignore(self, path):
"""Handle call to ensure .gitignore file exists and the
next append will be on a new line (this means an empty file
Expand Down
35 changes: 35 additions & 0 deletions jupyterlab_git/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,39 @@ async def post(self, path: str = ""):
self.finish(json.dumps(output))


class GitRemoteDetailsShowHandler(GitHandler):
"""Handler for 'git remote -v'."""

@tornado.web.authenticated
async def get(self, path: str = ""):
"""GET request handler to retrieve existing remotes."""
local_path = self.url2localpath(path)
output = await self.git.remote_show(local_path, verbose=True)
if output["code"] == 0:
self.set_status(201)
BoscoCHW marked this conversation as resolved.
Show resolved Hide resolved
else:
self.set_status(500)
self.finish(json.dumps(output))


class GitRemoteRemoveHandler(GitHandler):
"""Handler for 'git remote remote <name>'."""

@tornado.web.authenticated
async def post(self, path: str = ""):
"""GET request handler to remove a remote."""
local_path = self.url2localpath(path)
data = self.get_json_body()
name = data.get("name", None)

output = await self.git.remote_remove(local_path, name)
if output["code"] == 0:
self.set_status(201)
else:
self.set_status(500)
self.finish(json.dumps(output))
fcollonval marked this conversation as resolved.
Show resolved Hide resolved


class GitResetHandler(GitHandler):
"""
Handler for 'git reset <filename>'.
Expand Down Expand Up @@ -871,6 +904,8 @@ def setup_handlers(web_app):
("/push", GitPushHandler),
("/remote/add", GitRemoteAddHandler),
("/remote/fetch", GitFetchHandler),
("/remote/show", GitRemoteDetailsShowHandler),
("/remote/remove", GitRemoteRemoveHandler),
fcollonval marked this conversation as resolved.
Show resolved Hide resolved
("/reset", GitResetHandler),
("/reset_to_commit", GitResetToCommitHandler),
("/show_prefix", GitShowPrefixHandler),
Expand Down
83 changes: 68 additions & 15 deletions src/commandsAndMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { JupyterFrontEnd } from '@jupyterlab/application';
import {
Dialog,
InputDialog,
MainAreaWidget,
ReactWidget,
showDialog,
Expand Down Expand Up @@ -47,7 +46,9 @@ import {
} from './tokens';
import { GitCredentialsForm } from './widgets/CredentialsBox';
import { discardAllChanges } from './widgets/discardAllChanges';
import { AddRemoteDialogue } from './components/AddRemoteDialogue';
import { CheckboxForm } from './widgets/GitResetToRemoteForm';
import { SingleSelectionForm } from './widgets/SelectRemoteForm';

export interface IGitCloneArgs {
/**
Expand Down Expand Up @@ -265,16 +266,36 @@ export function addCommands(
return;
}
let url = args['url'] as string;
const name = args['name'] as string;
let name = args['name'] as string;

if (!url) {
const result = await InputDialog.getText({
title: trans.__('Add a remote repository'),
placeholder: trans.__('Remote Git repository URL')
});
const widgetId = 'git-dialog-AddRemote';
let anchor = document.querySelector<HTMLDivElement>(`#${widgetId}`);
if (!anchor) {
anchor = document.createElement('div');
anchor.id = widgetId;
document.body.appendChild(anchor);
}

const waitForDialog = new PromiseDelegate<Git.IGitRemote | null>();
const dialog = ReactWidget.create(
<AddRemoteDialogue
trans={trans}
model={gitModel}
onClose={(remote?: Git.IGitRemote) => {
dialog.dispose();
waitForDialog.resolve(remote ?? null);
}}
/>
);

Widget.attach(dialog, anchor);

const remote = await waitForDialog.promise;

if (result.button.accept) {
url = result.value;
if (remote) {
name = remote.name;
url = remote.url;
}
}

Expand Down Expand Up @@ -311,16 +332,40 @@ export function addCommands(
caption: trans.__('Push code to remote repository'),
isEnabled: () => gitModel.pathRepository !== null,
execute: async args => {
logger.log({
level: Level.RUNNING,
message: trans.__('Pushing…')
});
try {
const remotes = await gitModel.getRemotes();

let remote;
if (remotes.length > 1) {
const result = await showDialog({
title: trans.__('Pick a remote repository to push.'),
body: new SingleSelectionForm(
trans.__(''),
remotes.map(remote => remote.name)
),
buttons: [
Dialog.cancelButton({ label: trans.__('Cancel') }),
Dialog.okButton({ label: trans.__('Proceed') })
]
});
if (result.button.accept) {
remote = result.value.selection;
} else {
return;
}
}

logger.log({
level: Level.RUNNING,
message: trans.__('Pushing…')
});
const details = await showGitOperationDialog(
gitModel,
args.force ? Operation.ForcePush : Operation.Push,
trans
trans,
(args = { remote })
);

logger.log({
message: trans.__('Successfully pushed'),
level: Level.SUCCESS,
Expand Down Expand Up @@ -1512,10 +1557,18 @@ export async function showGitOperationDialog<T>(
result = await model.pull(authentication);
break;
case Operation.Push:
result = await model.push(authentication);
result = await model.push(
authentication,
false,
(args as unknown as { remote: string })['remote']
);
break;
case Operation.ForcePush:
result = await model.push(authentication, true);
result = await model.push(
authentication,
true,
(args as unknown as { remote: string })['remote']
);
break;
case Operation.Fetch:
result = await model.fetch(authentication);
Expand Down
Loading